當前位置:
首頁 > 知識 > netty整合springMVC,實現高效的HTTP服務請求

netty整合springMVC,實現高效的HTTP服務請求

首先,你必須要了解netty,說簡單點:客戶端通過TCP鏈接和伺服器建立長連接,client和server都是通過管道(ChannelPipeline)的addLast方法的添加順序來處理接收或者發送的數據。這個和struts的filter的doFilter原理類似,處理完一個filter,如果後面還有其他的filter,就將數據chain.doFilter來繼續處理。

然後,說說netty怎麼來整合springMVC:當client和server建立連接後,我們在addLast的某個類中將client發來的請求,讓DispatcherServlet來處理,然後將處理後的結果通過ChannelHandlerContext或者Channel將,結果writeAndFlush到client。

1.寫一個netty sever的java代碼

package com.magic.netty.server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.DispatcherServlet;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import com.magic.netty.HttpServerInitializer;
public class NettyHttpServer {
public NettyHttpServer(Integer port) {
this.port = port;
}
public NettyHttpServer(Integer port, DispatcherServlet servlet) {
this.port = port;
this.servlet = servlet;
}
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new HttpServerInitializer(servlet))
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
System.out.println("NettyHttpServer Run successfully");
// 綁定埠,開始接收進來的連接
ChannelFuture f = b.bind(port).sync();
// 等待伺服器 socket 關閉 。在這個例子中,這不會發生,但你可以優雅地關閉你的伺服器。
f.channel().closeFuture().sync();
} catch (Exception e) {
log.error("NettySever start fail",e);
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
private int port;
private static Logger log = LoggerFactory.getLogger(NettyHttpServer.class);
private DispatcherServlet servlet;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

2.初始化netty的channel管道

package com.magic.netty;
import org.springframework.web.servlet.DispatcherServlet;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
public HttpServerInitializer(DispatcherServlet servlet) {
this.servlet = servlet;
}
public HttpServerInitializer() {
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("aggregator", new HttpObjectAggregator(2147483647));
pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());
pipeline.addLast("deflater", new HttpContentCompressor());
pipeline.addLast("handler", new HttpRequestHandler(servlet));
}
private DispatcherServlet servlet;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

3.在handler裡面處理client發來的請求

package com.magic.netty;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletContext;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
import io.netty.handler.codec.http.multipart.MemoryAttribute;
import io.netty.util.CharsetUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils;
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
public HttpRequestHandler(DispatcherServlet servlet) {
this.servlet = servlet;
this.servletContext = servlet.getServletConfig().getServletContext();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception {
logger.error(e.getMessage(),e);
ctx.close();
}
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) throws Exception {
boolean flag = HttpMethod.POST.equals(fullHttpRequest.getMethod())
|| HttpMethod.GET.equals(fullHttpRequest.getMethod());
Map<String, String> parammap = getRequestParams(ctx,fullHttpRequest);
if(flag && ctx.channel().isActive()){
//HTTP請求、GET/POST
MockHttpServletResponse servletResponse = new MockHttpServletResponse();
MockHttpServletRequest servletRequest =new MockHttpServletRequest(servletContext);
// headers
for (String name : fullHttpRequest.headers().names()) {
for (String value : fullHttpRequest.headers().getAll(name)) {
servletRequest.addHeader(name, value);
}
}
String uri = fullHttpRequest.getUri();
uri = new String(uri.getBytes("ISO8859-1"), "UTF-8");
uri = URLDecoder.decode(uri, "UTF-8");
UriComponents uriComponents = UriComponentsBuilder.fromUriString(uri).build();
String path = uriComponents.getPath();
path = URLDecoder.decode(path, "UTF-8");
servletRequest.setRequestURI(path);
servletRequest.setServletPath(path);
servletRequest.setMethod(fullHttpRequest.getMethod().name());
if (uriComponents.getScheme() != null) {
servletRequest.setScheme(uriComponents.getScheme());
}
if (uriComponents.getHost() != null) {
servletRequest.setServerName(uriComponents.getHost());
}
if (uriComponents.getPort() != -1) {
servletRequest.setServerPort(uriComponents.getPort());
}
ByteBuf content = fullHttpRequest.content();
content.readerIndex(0);
byte[] data = new byte[content.readableBytes()];
content.readBytes(data);
servletRequest.setContent(data);
try {
if (uriComponents.getQuery() != null) {
String query = UriUtils.decode(uriComponents.getQuery(),"UTF-8");
servletRequest.setQueryString(query);
}
if(parammap!=null&&parammap.size()>0){
for (String key : parammap.keySet()) {
servletRequest.addParameter(UriUtils.decode(key,"UTF-8"), UriUtils.decode(parammap.get(key) == null ? "": parammap.get(key), "UTF-8"));
}
}
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}
this.servlet.service(servletRequest,servletResponse);
HttpResponseStatus status = HttpResponseStatus.valueOf(servletResponse.getStatus());
String result = servletResponse.getContentAsString();
result = StringUtils.isEmpty(result)?"":result;
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status,Unpooled.copiedBuffer(result,CharsetUtil.UTF_8));
response.headers().set("Content-Type", "text/json;charset=UTF-8");
response.headers().set("Access-Control-Allow-Origin", "*");
response.headers().set("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With,X-File-Name");
response.headers().set("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
response.headers().set("Content-Length", Integer.valueOf(response.content().readableBytes()));
response.headers().set("Connection", "keep-alive");
ChannelFuture writeFuture = ctx.writeAndFlush(response);
writeFuture.addListener(ChannelFutureListener.CLOSE);
}
}
/**
* 獲取post請求、get請求的參數保存到map中
*/
private Map<String, String> getRequestParams(ChannelHandlerContext ctx, HttpRequest req){
Map<String, String>requestParams=new HashMap<String, String>();
// 處理get請求
if (req.getMethod() == HttpMethod.GET) {
QueryStringDecoder decoder = new QueryStringDecoder(req.getUri());
Map<String, List<String>> parame = decoder.parameters();
Iterator<Entry<String, List<String>>> iterator = parame.entrySet().iterator();
while(iterator.hasNext()){
Entry<String, List<String>> next = iterator.next();
requestParams.put(next.getKey(), next.getValue().get(0));
}
}
// 處理POST請求
if (req.getMethod() == HttpMethod.POST) {
HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(
new DefaultHttpDataFactory(false), req);
List<InterfaceHttpData> postData = decoder.getBodyHttpDatas(); //
for(InterfaceHttpData data:postData){
if (data.getHttpDataType() == HttpDataType.Attribute) {
MemoryAttribute attribute = (MemoryAttribute) data;
requestParams.put(attribute.getName(), attribute.getValue());
}
}
}
return requestParams;
}
private static final Logger logger = LoggerFactory.getLogger(HttpRequestHandler.class);
private final DispatcherServlet servlet;
private final ServletContext servletContext;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

4.初始化servlet並啟動netty server

package com.magic;
import javax.servlet.ServletException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import com.magic.common.config.PropConfig;
import com.magic.netty.server.NettyHttpServer;
public class MagicWebServer {
private static Logger logger = LoggerFactory.getLogger(MagicWebServer.class);
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Integer port = 6001;
DispatcherServlet servlet = getDispatcherServlet(ctx);
NettyHttpServer server = new NettyHttpServer(port,servlet);
server.start();
}
public static DispatcherServlet getDispatcherServlet(ApplicationContext ctx){
XmlWebApplicationContext mvcContext = new XmlWebApplicationContext();
mvcContext.setConfigLocation("classpath:spring-servlet.xml");
mvcContext.setParent(ctx);
MockServletConfig servletConfig = new MockServletConfig(mvcContext.getServletContext(), "dispatcherServlet");
DispatcherServlet dispatcherServlet = new DispatcherServlet(mvcContext);
try {
dispatcherServlet.init(servletConfig);
} catch (ServletException e) {
e.printStackTrace();
}
return dispatcherServlet;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

5.寫一個controller,並測試一波http://127.0.0.1:6001/user/login

package com.magic.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSONObject;
@Controller
@RequestMapping(value="/user",produces = "text/json;charset=utf-8")
public class UserController extends BaseController{
@RequestMapping("/login")
@ResponseBody
public String login(String username,String pwd){
JSONObject resultJson = new JSONObject();
Map<String, String> loginResult = new HashMap<String, String>();
loginResult.put("username", username);
loginResult.put("age", "20");
loginResult.put("sex", "boy");
resultJson.put("code", 200);
resultJson.put("msg", "登錄成功");
resultJson.put("result", loginResult);
return JSONObject.toJSONString(resultJson);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

Mark一下,說明在coding的時候遇到的問題

  1. 代碼中的applicationContext.xml和spring-servlet.xml的按照springMVC的正常配置就行
  2. 如果返回到client端的代碼有中午亂碼,那麼在requestMapping裡面添加produces = 「text/json;charset=utf-8」。

netty整合springMVC,實現高效的HTTP服務請求

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

Discuz全版本任意文件刪除漏洞
Jenkins+RobotFramework持續集成測試-jenkins環境搭建

TAG:程序員小新人學習 |