netty做HTTP和websocket后端服务

提供一种netty同时做HTTP和websocket容器,并实现HTTP路由的思路

netty学习资源

  1. 官网的API文档
  2. 《netty权威指南》,给很多步骤做出了解释,没解释的看API
  3. 官方的demo,下载netty-example-5.0.0.Alpha2-sources.jar源码包解压,给出了很多Demo
  4. Github上netty项目很多,分析下源码

server端编码

  1. HttpServerCodec: ByteBuf->HttpRequest或HttpContent和HttpResponse或者HttpContent->ByteBuf,即HTTP请求的解码和编码
  2. HttpObjectAggregator: 把多个消息转换为一个单一的FullHttpRequest或是FullHttpResponse,原因是HTTP解码器会在每个HTTP消息中生成多个消息对象HttpRequest/HttpResponse,HttpContent,LastHttpContent
  3. WebSocketServerCompressionHandler: WebSocket数据压缩(可选)
  4. ChunkedWriteHandler: 大文件支持(没有写在代码中,可选)
  5. HttpHandler: 自定义的HTTP和Websocket处理类,二者处理也可以分开在多个ChannelHandler中,在这里写在一个Handler中
    • handler方法和childHandler方法区别
    • handler存在于AbstractBootstrap,目的是添加一个handler,监听Bootstrap动作,它在初始化时就会执行
    • childHandler存在于ServerBootstrap,目的是添加一个handler,监听已经连接的客户端的Channel的动作和状态,在客户端连接成功后才执行。
    • option方法和childOption方法区别
    • option存在于AbstractBootstrap,提供给NioServerSocketChannel用来接收进入的连接。
    • childOption存在于ServerBootstrap ,提供给ServerChannel接收已经建立的连接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public void run(final int port) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast("respDecoder-reqEncoder",
new HttpServerCodec())
.addLast("http-aggregator",
new HttpObjectAggregator(65536))
.addLast(new WebSocketServerCompressionHandler())
.addLast("action-handler", new HttpHandler());
}
});
ChannelFuture future = b.bind(new InetSocketAddress(port)).sync();
logger.info("The http server powered by netty is listening on " + port);
future.channel().closeFuture().sync();//阻塞处理,等待服务端链路关闭之后main函数才退出
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}

自定义ChannelHandler,用于处理HTTP和Websocket

  1. 区别HTTP和Websocket请求

    1
    2
    3
    4
    5
    6
    7
    8
     @Override
    public void messageReceived(ChannelHandlerContext ctx, Object msg) {
    if (msg instanceof FullHttpRequest) {//如果是HTTP请求,进行HTTP操作
    handleHttpRequest(ctx, (FullHttpRequest) msg);
    } else if (msg instanceof WebSocketFrame) {//如果是Websocket请求,则进行websocket操作
    handleWebSocketFrame(ctx, (WebSocketFrame) msg);
    }
    }

    上面的Object类型的msg就是extends SimpleChannelInboundHandler中的Object,我们可以定义ChannelHandler时将Object换成FullHttpRequest,这样就只处理HTTP请求,换成WebSocketFrame只处理websocket

  2. HTTP请求的分发

  3. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //处理HTTP的代码
    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
    HttpMethod method=req.method();
    String uri=req.uri();
    diapathcer(method,uri);
    }
    private void diapathcer(HttpMethod method,String uri){
    if(method==HttpMethod.GET&&"/login".equals(uri)){
    //....处理
    }else if(method==HttpMethod.POST&&"/register".equals(uri)){
    //...处理
    }

    }
    1. 实际中路由不会这样写,我们可以自定义路由的实现,写在一个类中专门实现路由,或者自定义一个配置文件,像properties一样,每一行格式形如:(GET \login LoginAction),我们知道properties是继承HashTable(String,String),我们可以自定义配置文件的对应类继承HashMap(String,HashMap(String,String))
    2. 自定义ChannelHandler代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class HttpHandler extends SimpleChannelInboundHandler<Object> {
    @Override
    public void messageReceived(ChannelHandlerContext ctx, Object msg) {
    if (msg instanceof FullHttpRequest) {//如果是HTTP请求,进行HTTP操作
    handleHttpRequest(ctx, (FullHttpRequest) msg);
    } else if (msg instanceof WebSocketFrame) {//如果是Websocket请求,则进行websocket操作
    handleWebSocketFrame(ctx, (WebSocketFrame) msg);
    }
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
    ctx.flush();
    }
    //处理HTTP的代码
    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
    HttpMethod method=req.method();
    String uri=req.uri();
    diapathcer(method,uri);
    }
    private void diapathcer(HttpMethod method,String uri){
    if(method==HttpMethod.GET&&"/login".equals(uri)){
    //....处理
    }else if(method==HttpMethod.POST&&"/register".equals(uri)){
    //...处理
    }

    }
    //处理Websocket的代码
    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
    }
    }

     目录