Netty HTTP服务的实现样例


=Start=

缘由:

Netty 是一个高性能、异步事件驱动的 NIO 框架。在学习 Netty 的时候,发现 Netty 也可以实现HTTP服务,于是把学习的过程记录整理一下形成一篇文章,方便以后参考。

正文:

参考解答:

所有的 Netty 服务器都需要以下2个部分:

  1. ServerBootstrap :这是配置服务器的启动代码。它会将服务器绑定到它要监听连接请求的端口上;
  2. 至少一个 ChannelHandler :该组件实现了服务器对从客户端接收的数据的处理,即它的业务逻辑

这里将Netty服务器放在了3个class中,分别是:

  1. NettyServer #创建ServerBootstrap用于引导服务器
  2. NettyServerChannel #新建channel并用handler进行初始化(可简化放在NettyServer中)
  3. NettyServerHandler #通过覆写部分方法实现具体的业务逻辑

Netty服务器的引导过程:

  • 创建 ServerBootstrap 的实例来引导和绑定服务器。
  • 创建 NioEventLoopGroup 进行事件的处理,包括接收新的连接,处理读写
  • 使用同一个NettyServerHandler实例初始化每一个新的Channel
  • bind()绑定服务器端口进行监听。
package com.ixyzero.learn.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class NettyServer {
    private static final int port = 8081; //设置服务监听端口
    private static  ServerBootstrap b = new ServerBootstrap();
    private static  EventLoopGroup group = new NioEventLoopGroup();   // 通过NIO方式来接收连接和处理连接

    /**
     * Netty创建全部都是实现自 AbstractBootstrap。
     * 客户端的是 Bootstrap,服务端的则是 ServerBootstrap。
     **/
    public static void main(String[] args) throws InterruptedException {
        try {
            b.group(group);
            b.channel(NioServerSocketChannel.class);
            b.childHandler(new NettyServerChannel()); //为新的连接创建新的channel
            // 服务器绑定端口监听
            ChannelFuture f = b.bind(port).sync();
            System.out.println("服务端启动成功,端口是: "+port);
            // 监听服务器关闭监听
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程
        }
    }
}

&

在上面的NettyServer.java里有一个childHandler()调用,用于添加 NettyServerHandler 到 Channel 的 ChannelPipeline ,是通过新建这里的 NettyServerChannel 类(继承自ChannelInitializer)实现的。

package com.ixyzero.learn.netty;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;

public class NettyServerChannel extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline ph = ch.pipeline();
        //处理http服务的关键handler
        ph.addLast("encoder",new HttpResponseEncoder());
        ph.addLast("decoder",new HttpRequestDecoder());
        ph.addLast("aggregator", new HttpObjectAggregator(10*1024*1024));
        ph.addLast("handler", new NettyServerHandler());// 服务端业务逻辑
    }
}

&

由于我们的样例很简单,只需要继承 ChannelInboundHandlerAdapter 就行了。这个类提供了默认 ChannelInboundHandler 的实现,所以只需要部分覆写下面的方法:

  • channelRead() – 每个信息入站都会调用;
  • channelReadComplete() – 通知处理器最后的 channelread() 是当前批处理中的最后一条消息时调用;
  • exceptionCaught()- 读操作时捕获到异常时调用;
package com.ixyzero.learn.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.CharsetUtil;
import java.net.InetAddress;

public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    private String result="";
    /*
     * 收到消息时,返回信息
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(! (msg instanceof FullHttpRequest)){
            result = "未知请求!";
            System.out.println("msg not FullHttpRequest");
            send(ctx, result, HttpResponseStatus.BAD_REQUEST);
            return;
        }
        FullHttpRequest httpRequest = (FullHttpRequest)msg;
        try{
            String path = httpRequest.uri();        //获取路径
            System.out.println("URI: " + path);

            String body = getBody(httpRequest);     //获取参数
            HttpMethod method = httpRequest.method();//获取请求方法

            //如果不是这个路径,就直接返回错误
            if(!"/test".equalsIgnoreCase(path)){
                result="非法请求!";
                send(ctx, result, HttpResponseStatus.BAD_REQUEST);
                return;
            }
            System.out.println("接收到:"+method+" 请求");
            //如果是GET请求
            if(HttpMethod.GET.equals(method)){
                //接受到的消息,做业务逻辑处理...
                System.out.println("body:"+body);
                result="GET请求";
                send(ctx, result, HttpResponseStatus.OK);
                return;
            }
            //如果是POST请求
            if(HttpMethod.POST.equals(method)){
                //接受到的消息,做业务逻辑处理...
                System.out.println("body:"+body);
                result="POST请求";
                send(ctx, result, HttpResponseStatus.OK);
                return;
            }

            //如果是PUT请求
            if(HttpMethod.PUT.equals(method)){
                //接受到的消息,做业务逻辑处理...
                System.out.println("body:"+body);
                result="PUT请求";
                send(ctx, result, HttpResponseStatus.OK);
                return;
            }
            //如果是DELETE请求
            if(HttpMethod.DELETE.equals(method)){
                //接受到的消息,做业务逻辑处理...
                System.out.println("body:"+body);
                result="DELETE请求";
                send(ctx, result, HttpResponseStatus.OK);
                return;
            }
        }catch(Exception e){
            System.out.println("处理请求失败!");
            e.printStackTrace();
        }finally{
            //释放请求
            httpRequest.release();
        }
    }

    /**
     * 获取body参数
     * @param request
     * @return
     */
    private String getBody(FullHttpRequest request){
        ByteBuf buf = request.content();
        return buf.toString(CharsetUtil.UTF_8);
    }

    /**
     * 发送的返回值
     * @param ctx	  返回
     * @param context 消息
     * @param status 状态
     */
    private void send(ChannelHandlerContext ctx, String context,HttpResponseStatus status) {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(context, CharsetUtil.UTF_8));
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    /*
     * 建立连接时,返回消息
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress());
        ctx.writeAndFlush("客户端"+ InetAddress.getLocalHost().getHostName() + "成功与服务端建立连接! ");
        super.channelActive(ctx);
    }
}

 

参考链接:

=END=


《“Netty HTTP服务的实现样例”》 有 3 条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注