Netty做http服务端例程

原创
2017/01/12 15:55
阅读数 138

工作中需要Netty做一个Http的服务端,用来接收Http请求,并同步返回结果。中间遇到的问题就是数据处理完成后,不知该如何返回结果。发了一条动弹提问,非常感谢@programtic、@zigzagroad两位的回答,尤其感谢@zigzagroad,成功的解决了我的问题。在此,将代码分享出来,供大家交流,本人菜鸟一个,不喜勿喷,希望各大神多多指教。

这个服务端比较简单,netty使用4.1.1Final版本,主要有3个类TestHttpServer(服务端启动类)、InboundHandler、OutboundHandler。代码如下:

1.TestHttpServer

package cn.thinkit.bigdata.test;

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.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;

/**
 * Created by thinkit on 2017/1/11.
 */
public class TestHttpServer {
    public void start(int port) throws Exception {
        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
                        public void initChannel(SocketChannel ch) throws Exception {
                            //请求解析
                            ch.pipeline().addLast(new HttpRequestDecoder());
                            //这个是网上搜到的,解决重复处理同一个请求
                            ch.pipeline().addLast(new HttpObjectAggregator(65536));
                            //响应组装
                            ch.pipeline().addLast(new HttpResponseEncoder());
                            //输出数据handler
                            ch.pipeline().addLast(new OutboundHandler());
                            //接收数据handler
                            ch.pipeline().addLast(new InboundHandler());
                        }
                    }).option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture f = b.bind(port).sync();

            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        TestHttpServer server = new TestHttpServer();
        server.start(8765);
    }
}

 

2.InboundHandler

package cn.thinkit.bigdata.test;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpContent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Created by thinkit on 2017/1/11.
 */
public class InboundHandler extends ChannelInboundHandlerAdapter {
    private static final Logger logger = LoggerFactory.getLogger(InboundHandler.class);

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        logger.info("InboundHandler.channelRead: ctx :" + ctx);
        //经过请求解析后,msg就是HttpContent,可以拿到请求参数
        if (msg instanceof HttpContent) {
            //输出接收到请求的时间
            Long startTime = System.currentTimeMillis();
            logger.info(Thread.currentThread().getName()+"收到http请求时间:" + startTime);

            //读取请求信息
            HttpContent content = (HttpContent) msg;
            ByteBuf buf = content.content();
            String requestInfo = buf.toString(io.netty.util.CharsetUtil.UTF_8);
            buf.release();
            logger.info("请求信息:" + requestInfo);

            //传给下个Handler
            ctx.write(requestInfo);
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        logger.info("InboundHandler.channelReadComplete");
        ctx.close();
    }
}

3.OutboundHandler

package cn.thinkit.bigdata.test;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Created by thinkit on 2017/1/11.
 */
public class OutboundHandler extends ChannelOutboundHandlerAdapter {
    private static final Logger logger = LoggerFactory.getLogger(OutboundHandler.class);
    @Override
    // 向client发送消息
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        logger.info("OutboundHandler.write");
        logger.info("InboundHandler传递信息:" + msg);

        //处理结果
        String result="hello";

        //处理请求数据过程,请求参数为json格式
        JSONArray jsonArray = JSONArray.fromObject("["+msg+"]");
        if (jsonArray.size() > 0) {

            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = JSONObject.fromObject(jsonArray.get(i));
                /*
                    此处可以对json信息做处理
                 */
            }
        }

        /*
            重点是这里,注释代码是之前根据官网例子写的,无法成功返回处理结果
            未注释的代码是@zigzagroad提供给我的解决办法,测试后可以返回结果
            目前不清楚原因。
         */
//        ByteBuf encoded = ctx.alloc().buffer(4 * result.length());
//        encoded.writeBytes(result.getBytes());
//        DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,encoded);
        ByteBuf res = Unpooled.copiedBuffer(result.getBytes());
        DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,res);


        ctx.writeAndFlush(response);
    }

}
展开阅读全文
打赏
0
0 收藏
分享
加载中

引用来自“programtic”的评论

注释和未注释的代码,我这里运行,浏览器访问时都可以看到hello
对了,我想起来了,之前是用Postman工具发的http请求,没有写response封装,也就是没有以下代码:
//响应组装
ch.pipeline().addLast(new HttpResponseEncoder());

DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,res);
// DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,encoded);

这样写的时候,注释代码是没有返回结果的,后来我又写了一个http客户端程序用来发送请求,接收报错,说响应不可用,我才加的response封装代码。这样看来,应该后来的写法是比较正规的,之前可能是由于工具具有容错性,才能接收到结果吧。
2017/01/13 11:12
回复
举报

引用来自“programtic”的评论

注释和未注释的代码,我这里运行,浏览器访问时都可以看到hello

那这就尴尬了…;博主有时间的话并且愿意的话 可以研究确认一下是哪里的问题
2017/01/12 20:37
回复
举报
注释和未注释的代码,我这里运行,浏览器访问时都可以看到hello
2017/01/12 17:59
回复
举报
更多评论
打赏
3 评论
0 收藏
0
分享
返回顶部
顶部