Netty挖掘机(三)结合Spring搭建Netty脚手架

上文《Netty挖掘机(二)初识Netty》,主要介绍了Netty的特性及其如何启动,启动的相关配置说明。这一篇主要讲一下如何结合Spring搭建Netty脚手架,实现API接口服务模块。

结合Spring依赖注入的特性,在Netty的handler中获取上下文的bean,再通过uri找到匹配的方法。

maven pom配置

主要引入了Netty、Spring的依赖, 及其日志框架logback,很简单。

封装请求体

@Data
public class ApiReq {
    /**
     * HTTP protocol
     */
    private String protocol;
    /**
     * ip
     */
    private String remoteIp;
    /**
     * request type
     */
    private String method;
    /**
     * request uri
     */
    private String uri;
    /**
     * request headers
     */
    private Map headers;
    /**
     * request data
     */
    private Map data = Maps.newHashMap();
    private String controllerName;
    private String invokeMethodName;
    public String getParam(String key) {
        return data.get(key);
    }
}

封装返回体

@Data
@Slf4j
public class ApiResp implements Serializable {

    private static final long serialVersionUID = -8826517176378050058L;
    private int result = 0;
    private String msg;
    private String data;
    public ApiResp() {}

    public static ApiResp create(String data){
        ApiResp ret = new ApiResp();
        ret.setMsg("success");
        ret.setData(data);
        return ret;
    }
}

定义Controller的基类,用于后续反射调用派生类方法

public class ApiController {
    public ApiResp invoke(String invokeMethodName, ApiReq req)  {
        try {
            Method method = getClass().getMethod(invokeMethodName, req.getClass());
            ApiResp result = (ApiResp) method.invoke(this, req);
            return result;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

定义一个功能类

用户功能类,这里只列出一个方法,返回简单的文字描述信息。

public class UserController extends ApiController {
    public ApiResp get(ApiReq req) {
        return ApiResp.create("my name is " + req.getParam("name"));
    }
}

实现一个Netty 启动类

回顾一下上文讲到的启动类,其实启动类对外透明化,只要很简单的几行代码,就帮我们实现了很多配置,甚至有更多的透明化配置等着我们去使用。

@Slf4j
public class HttpServer {
    private AbstractControllerAdapter controllerAdapter;
    public HttpServer(AbstractControllerAdapter controllerAdapter) {
        this.controllerAdapter = controllerAdapter;
    }
    static EventLoopGroup boss = new NioEventLoopGroup();
    static EventLoopGroup worker = new NioEventLoopGroup();
    public void start(int port) {
        log.info("############# start server at port: {}... #############", port);
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap
                // 绑定两个组
                .group(boss, worker)
                // 创建NioServerSocketChannel实例
                .channel(NioServerSocketChannel.class)
                // 添加处理器Handler
                .childHandler(new ChannelInitializer() {
                    @Override
                    protected void initChannel(SocketChannel channel) throws Exception {
                        // 为通道Channel进行初始化配置
                        ChannelPipeline pipeline = channel.pipeline();
                        pipeline.addLast(new HttpRequestDecoder(),
                                new HttpResponseEncoder(),
                                new HttpServerHandler(controllerAdapter)
                        );
                    }
                })
                // 服务于boss线程(accept connect)
                .option(ChannelOption.SO_BACKLOG, 1024)
                // 设置关闭tcp的Nagle算法(尽可能发送大块数据,避免网络中充斥着许多小数据块),要求高实时性
                .childOption(ChannelOption.TCP_NODELAY, true)
                // 设置启用心跳保活机制
                .childOption(ChannelOption.SO_KEEPALIVE, true);
            bootstrap.bind(port).sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

其中在初始化配置中,可以看到主要配置了三个

  • HttpRequestDecoder:将字节解码为HttpRequest、HttpContent和LastHttpContent消息;

  • HttpResponseEncoder:将HttpResponse、HttpContent和LastHttpContent消息编码为字节;

  • HttpServerHandler:服务中转站,下面会提到。

服务中转站

中转,即是一个协同者的角色,提供一个handler,拦截请求以执行入站和出战事件。

ChannelHandler是Netty中处理器的抽象,Netty对其提供了很多种可以开箱即用的实现,包括运用于各种协议(如HTTP SSL)的ChannelHandler,在内部也使用了事件和Future。

在这里我们用了其中的一个实现ChannelInboundHandlerAdapter来拦截和处理事件。其中涉及到Netty中对HTTP的执行流程的一个封装。

full_http.jpg

以上是一次完整的Http请求,HttpRequest是请求的第一部分,包含Http头部信息;HttpContent是请求中存放数据的块体,不止一个;LastHttpContent标志请求的结束,也包含数据,也可能包含尾随的Http头部信息。

按照这种顺序,接下来实现通过一次完整的Http请求获取数据

@Slf4j
public class HttpServerHandler extends ChannelInboundHandlerAdapter {
    private AbstractControllerAdapter adapter;
    private ApiReq req;
    public HttpServerHandler(AbstractControllerAdapter adapter) {
        this.adapter = adapter;
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        long startTime = System.currentTimeMillis();
        try {
            if(msg instanceof HttpRequest) {
                HttpRequest request = (HttpRequest) msg;
                req = ApiReq.decode(request);
            } else if(msg instanceof LastHttpContent) {
                String controllerName = req.getControllerName();
                HttpContent content = (HttpContent) msg;
                Map data = req.parseData(content);
                req.setData(data);
                FullHttpResponse response = ApiResp.response(adapter, req);
                ctx.write(response);
            }
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
        try {
            ctx.channel().flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }
}

到这里,基于Spring+Netty实现的API框架已成功搭建好了哈。

具体源码请查看github:

https://github.com/qJerry/Netty-Analyze-Demo/tree/master/Chapter1-2


Ending......

阿黑在下一章节将结合SpringBoot搭建Netty脚手架...!

你可能感兴趣的:(Netty挖掘机(三)结合Spring搭建Netty脚手架)