Netty整合SpringMVC,實(shí)現(xiàn)高效的HTTP服務(wù)請(qǐng)求
來(lái)源:blog.csdn.net/shzy1988/article/
details/78841140
首先,你必須要了解netty,說(shuō)簡(jiǎn)單點(diǎn):客戶端通過(guò)TCP鏈接和服務(wù)器建立長(zhǎng)連接,client和server都是通過(guò)管道(ChannelPipeline)的addLast方法的添加順序來(lái)處理接收或者發(fā)送的數(shù)據(jù)。
這個(gè)和struts的filter的doFilter原理類似,處理完一個(gè)filter,如果后面還有其他的filter,就將數(shù)據(jù)chain.doFilter來(lái)繼續(xù)處理。
然后,說(shuō)說(shuō)netty怎么來(lái)整合springMVC:當(dāng)client和server建立連接后,我們?cè)赼ddLast的某個(gè)類中將client發(fā)來(lái)的請(qǐng)求,讓DispatcherServlet來(lái)處理,然后將處理后的結(jié)果通過(guò)ChannelHandlerContext或者Channel將,結(jié)果writeAndFlush到client。
1.寫一個(gè)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");
???//?綁定端口,開始接收進(jìn)來(lái)的連接
???ChannelFuture?f?=?b.bind(port).sync();
???//?等待服務(wù)器 socket 關(guān)閉?。在這個(gè)例子中,這不會(huì)發(fā)生,但你可以優(yōu)雅地關(guān)閉你的服務(wù)器。
???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;
}
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;
}
3.在handler里面處理client發(fā)來(lái)的請(qǐng)求
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??parammap?=?getRequestParams(ctx,fullHttpRequest);
????????if(flag?&&?ctx.channel().isActive()){
????????????//HTTP請(qǐng)求、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&¶mmap.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請(qǐng)求、get請(qǐng)求的參數(shù)保存到map中
?????*/
????private?Map?getRequestParams(ChannelHandlerContext?ctx,?HttpRequest?req) {
????????MaprequestParams=new?HashMap();
????????//?處理get請(qǐng)求??
????????if?(req.getMethod()?==?HttpMethod.GET)?{
????????????QueryStringDecoder?decoder?=?new?QueryStringDecoder(req.getUri());??
????????????Map>?parame?=?decoder.parameters();??
????????????Iterator>>?iterator?=?parame.entrySet().iterator();
????????????while(iterator.hasNext()){
????????????????Entry>?next?=?iterator.next();
????????????????requestParams.put(next.getKey(),?next.getValue().get(0));
????????????}
????????}
?????????//?處理POST請(qǐng)求??
????????if?(req.getMethod()?==?HttpMethod.POST)?{
????????????HttpPostRequestDecoder?decoder?=?new?HttpPostRequestDecoder(??
????????????????????new?DefaultHttpDataFactory(false),?req);??
????????????List?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;
}
4.初始化servlet并啟動(dòng)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;
?}
}
5.寫一個(gè)controller,并測(cè)試一波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?loginResult?=?new?HashMap();
??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);
?}
}
Mark一下,說(shuō)明在coding的時(shí)候遇到的問(wèn)題
代碼中的 applicationContext.xml和spring-servlet.xml的按照springMVC的正常配置就行如果返回到client端的代碼有中文亂碼,那么在 requestMapping里面添加produces = “text/json;charset=utf-8”。
程序汪資料鏈接
程序汪接的7個(gè)私活都在這里,經(jīng)驗(yàn)整理
Java項(xiàng)目分享 最新整理全集,找項(xiàng)目不累啦 06版
堪稱神級(jí)的Spring Boot手冊(cè),從基礎(chǔ)入門到實(shí)戰(zhàn)進(jìn)階
臥槽!字節(jié)跳動(dòng)《算法中文手冊(cè)》火了,完整版 PDF 開放下載!
臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開放下載!
字節(jié)跳動(dòng)總結(jié)的設(shè)計(jì)模式 PDF 火了,完整版開放下載!
歡迎添加程序汪個(gè)人微信 itwang008? 進(jìn)粉絲群或圍觀朋友圈
