天天看點

netty實作http伺服器1. HttpServer過濾器action

1. HttpServer

HttpServer作為http伺服器的server端:

public final class HttpServer {

    private static final String IP = "127.0.0.1";
    private static final int PORT = 9000;
    private static final int BIZ_THREAD_SIZE = 100;

    private static final Logger logger = LoggerFactory.getLogger(HttpServer.class);

    public static void main(String[] args) throws Exception {
        System.out.println("啟動Server...");
        HttpServer.start();
    }

    public static void start() throws Exception {
        initServerConfig();

        // new NioEventLoopGroup()預設線程數為CPU核心數的兩倍(Runtime.getRuntime().availableProcessors() * 2),是以這裡不用額外指定。
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 這裡worker不能使用預設,如果并發大的話需要增加線程數量
        EventLoopGroup workerGroup = new NioEventLoopGroup(BIZ_THREAD_SIZE);

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    // ChannelOption.SO_BACKLOG對應的是tcp/ip協定listen函數中的backlog參數。函數listen(int socketfd, int backlog)用來初始化服務端可連接配接隊列。
                    // 服務端處理用戶端連接配接請求是順序處理的,是以同一時間隻能處理一個用戶端連接配接,多個用戶端來的時候,服務端将不能處理的用戶端連接配接請求放在隊列中等待處理,backlog參數指定了隊列的大小。
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childOption(ChannelOption.SO_KEEPALIVE, true) // 子channel配置,保持長連接配接
                    .childHandler(new HttpServerInitializer());

            // sync追蹤代碼可以得到:this.await(),實作阻塞
            ChannelFuture channelFuture = bootstrap.bind(IP, PORT).sync();

            // 啟動失敗則關閉線程組
            if (!channelFuture.isSuccess()) {
                shutdown(bossGroup, workerGroup);
                throw new RuntimeException(channelFuture.cause());
            }

            channelFuture.channel().closeFuture().sync();
            logger.info("bootstrap channel closed...");
        } finally {
            shutdown(bossGroup, workerGroup);
        }
    }

    // 添加路徑映射和過濾器映射
    private static void initServerConfig() {
        ServerContext.setFilter(ServerContext.MAPPING_ALL, BaseFilter.class);
        ServerContext.setFilter("/template", TemplateFilter.class);
        ServerContext.setAction(ServerContext.MAPPING_ALL, DefaultIndexAction.class);
        ServerContext.setAction("/template", TemplateAction.class);
        ServerContext.setAction("/files", FileAction.class);
        ServerContext.setROOT("root");
        ServerContext.setPORT(8090);
    }

    private static void shutdown(EventLoopGroup bossGroup, EventLoopGroup workerGroup) {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}           

HttpServerInitializer設定http編解碼器,以及請求aggregator等:

public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    public void initChannel(SocketChannel socketChannel) {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline
                //或者使用HttpRequestDecoder & HttpResponseEncoder
                .addLast(new HttpServerCodec())
                //把HttpObjectAggregator放入管道裡。HttpObjectAggregator會把多個消息轉換為一個單一的FullHttpRequest或是FullHttpResponse
                .addLast(new HttpObjectAggregator(1024 * 1024))
                //壓縮Http消息
//                            .addLast(new HttpChunkContentCompressor())
                //大檔案支援,檔案上傳時使用
//                            .addLast(new ChunkedWriteHandler())
                .addLast(new HttpServerExpectContinueHandler())
                .addLast(new HttpServerDispatcherHandler());
    }
}           

入棧處理器,用于轉發http請求,類似于DispatcherServlet:

public class HttpServerDispatcherHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        if (!(msg instanceof FullHttpRequest)) {
            return;
        }

        FullHttpRequest fullHttpRequest = (FullHttpRequest) msg;
        final Request customerRequest = Request.build(ctx, fullHttpRequest);
        final Response customerResponse = Response.build(ctx, customerRequest);
        if (customerRequest.getRequestUrl().equals(HttpHelper.FAVICON_ICO)) {
            return;
        }

        // 過濾器放行之後,執行action,實際處理業務邏輯
        boolean passFilter = doFilter(customerRequest, customerResponse);
        if (passFilter) {
            doAction(customerRequest, customerResponse);
        }

        //如果發送請求未被觸發,則觸發之,否則跳過。
        if(false == customerResponse.isSent()) {
            customerResponse.response();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    /**
     * 過濾器
     * @param request
     * @param response
     * @return
     */
    private boolean doFilter(Request request, Response response) {
        //全局過濾器
        Filter filter = ServerContext.getFilter(ServerContext.MAPPING_ALL);
        if(null != filter) {
            if(false == filter.doFilter(request, response)) {
                return false;
            }
        }

        //自定義過濾器
        filter = ServerContext.getFilter(request.getRequestUrl());
        if(null != filter) {
            if(false == filter.doFilter(request, response)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 請求處理器
     * 相當于servlet
     * @param request
     * @param response
     */
    private void doAction(Request request, Response response) {
        Action action = ServerContext.getAction(request.getRequestUrl());
        if (null == action) {
            //查找比對所有路徑的Action
            action = ServerContext.getAction(ServerContext.MAPPING_ALL);
            if(null == action) {
                // 非Action方法,調用靜态檔案讀取
                action = Singleton.get(FileAction.class);
            }
        }

        action.doAction(request, response);
    }
}           

過濾器

public interface Filter {
    
    /**
     * 執行過濾
     * @param request 請求對象
     * @param response 響應對象
     * @return 如果傳回true,則繼續執行下一步内容,否則中斷
     */
    boolean doFilter(Request request, Response response);
}           

過濾器示例:

public class TemplateFilter implements Filter {

    @Override
    public boolean doFilter(Request request, Response response) {
        System.out.println("welcome in template filter...");
        return true;
    }
}           

action

action用于實際處理業務,頂層接口:

public interface Action {

    void doAction(Request request, Response response);
}           

抽象Adapter:

public abstract class AbstractAction implements Action {

    @Override
    public void doAction(Request request, Response response) {
        String method = request.getMethod();
        if (HttpHelper.METHOD_GET.equals(method)) {
            doGet(request, response);
        } else if (HttpHelper.METHOD_POST.equals(method)) {
            doPost(request, response);
        } else if (HttpHelper.METHOD_PUT.equals(method)) {
            doPut(request, response);
        } else if (HttpHelper.METHOD_DELETE.equals(method)) {
            doDelete(request, response);
        } else if (HttpHelper.METHOD_HEAD.equals(method)) {
            doHead(request, response);
        } else if (HttpHelper.METHOD_OPTIONS.equals(method)) {
            doOptions(request, response);
        } else if (HttpHelper.METHOD_TRACE.equals(method)) {
            doTrace(request, response);
        }
    }

    protected void doGet(Request request, Response response) {}

    protected void doPost(Request request, Response response) {}

    protected void doPut(Request request, Response response) {}

    protected void doDelete(Request request, Response response) {}

    protected void doOptions(Request request, Response response) {}

    protected void doHead(Request request, Response response) {}

    protected void doTrace(Request request, Response response) {}
}           

action示例:

public class TemplateAction extends AbstractAction {

    @Override
    protected void doGet(Request request, Response response) {
        response.setContentType(HttpHelper.CONTENT_TYPE_JSON);
        response.setContent("welcome in template action, do get...");
    }

    @Override
    protected void doPost(Request request, Response response) {
        response.setContent("welcome in template action, do post...");
    }
}           

。。。

繼續閱讀