簡單的設計思路就是,啟動一個可以截斷並處理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了。