Netty练手项目-简单Http服务器
<!--netty依赖--> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.20.Final</version> </dependency>
简单的设计思路就是,启动一个可以截断并处理Http请求的服务器代码。使用netty提供的boss线程与worker线程的模型,并使用netty的http解码器。自行编写了http url处理的部分。在接口层面,使用json作为格式。
初始化扫描会指定扫描controller.container下的类,使用了自定义Annotation并且单例的方式加载,未考虑太多细节就为了好玩。
接下来使用postman尝试调用接口。
一个简单的netty搭建的web服务就完成了。
package server; import handler.HttpServerHandler; import initScan.InitialProcess; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpServerCodec; /** * Created by MacBook on 2019/7/14. */ public class ServerStarter { private EventLoopGroup boss ; private EventLoopGroup worker ; private int port; private static int DEFAULT_PORT = 8080; public ServerStarter(){ boss = new NioEventLoopGroup(); worker = new NioEventLoopGroup(); port = DEFAULT_PORT; } public ServerStarter(int port){ this(); if(port < 0 || port > 65535){ throw new IllegalArgumentException("port:"+port+" is illegal"); } this.port = port; } public void startup() throws InterruptedException{ if(boss.isShutdown() || worker.isShutdown()){ throw new IllegalStateException("server was closed"); } // 初始化容器 InitialProcess initialProcess = new InitialProcess(); initialProcess.init(); ServerBootstrap serverBootstrap = new ServerBootstrap(); System.out.println("=====http server start "); serverBootstrap.channel(NioServerSocketChannel.class) .group(boss,worker) .childOption(ChannelOption.SO_KEEPALIVE,true) .option(ChannelOption.SO_BACKLOG,1024) .childHandler(new ChannelInitializer<SocketChannel>() { protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast("http-decode",new HttpServerCodec()); socketChannel.pipeline().addLast(new HttpServerHandler()); } }); ChannelFuture future = serverBootstrap.bind(port).sync(); System.out.println("server port listen:"+port); future.channel().closeFuture().sync(); shutDownGracefully(); } /** * 关闭管道 */ public void shutDownGracefully(){ this.boss.shutdownGracefully(); this.worker.shutdownGracefully(); } }
这是服务器启动的核心代码,首先初始化两个Nio线程组,把它们绑定到ServerBootstrap里,并添加Handler。
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; import com.alibaba.fastjson.serializer.SerializeConfig; import com.alibaba.fastjson.serializer.SerializerFeature; import controller.Properties; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.http.*; import response.BaseResponse; import java.lang.reflect.Method; /** * Created by MacBook on 2019/7/14. */ public class HttpServerHandler extends ChannelInboundHandlerAdapter{ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if(msg instanceof HttpRequest){ HttpRequest request = (HttpRequest)msg; // boolean keepAlive = HttpUtil.isKeepAlive(request); System.out.println("http request method :"+request.method()); System.out.println("http request uri : "+request.uri()); Object controller = Properties.urlController.get(request.uri()); Method m = Properties.urlMethod.get(request.uri()); if(controller == null){ FullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND); ctx.writeAndFlush(httpResponse).addListener(ChannelFutureListener.CLOSE); }else { FullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json"); Object result = m.invoke(controller); httpResponse.content().writeBytes(JSON.toJSONString(result,SerializerFeature.WriteMapNullValue).getBytes()); ctx.writeAndFlush(httpResponse).addListener(ChannelFutureListener.CLOSE); } } } }
这是处理器的代码,在经过第一个Http解码器之后,进来的对象msg已经被转化为HttpRequest了。