Netty-使用http进行通讯

具体原理解析:

https://blog.csdn.net/xiangzhihong8/article/details/52029446/

0 netty与tomcat的异同

摘自https://www.oschina.net/question/1765708_158026

我的架构最开始是 nginx + [netty (tcp) ...] 用nginx做负载均衡。后来分布式接口变成了http restful -> nginx + [netty(http) ...]

于是参考了netty的官方http例子,开始移植代码,其实移植量不大,只需要把原来的tpc handler里面的逻辑放到http handler里面就可以了。本以为ok了,测试才发现了大量的问题,其实问题本身是由于对http基础掌握的还不够透彻,比如keep-alive属性,这个平时基本不用的东西,当你要自己开发http server时,变的很有用,你不能每次都close channel.你要化很多时间了解header里面每个字段的意思,然后去实现它。再比如一个post请求,你要自己写代码从body里面读数据,解码等。

最终我放弃了netty,回到了tomcat.应为tomcat把http的一切都帮你搞定了。

最终的架构是 nginx + [tomcat(http) ...]

tomcat就是针对http层的,所以我建议http还是选择tomcat(或者其他成熟的http-server),并不是说netty不好,而是你的选择问题,netty是一个网络组件,tcp,udp,http都可以弄,但是官方文档都是些hello wolrd级别的。如果你非常了解http结构,完全可以基于netty搞出一个比tomcat牛的http server.

如果做tcp开发,netty不二之选!

总结为:

Netty是基于Java NIO开发的,而Tomcat是Apache下的针对HTTP的服务器项目,前者更像一个中间件框架,后者更像一个工具

理解:netty自己内部用,tomcat对外。

1、request的流程处理

1.1 实现:

   只需要在netty的pipeLine中配置HttpRequestDecoder和HttpObjectAggregator。

1.2 原理:

  •   1:如果把解析这块理解是一个黑盒的话,则输入是ByteBuf,输出是FullHttpRequest。通过该对象便可获取到所有与http协议有关的信息。
  •    2:HttpRequestDecoder先通过RequestLine和Header解析成HttpRequest对象,传入到HttpObjectAggregator。然后再通过body解析出httpContent对象,传入到HttpObjectAggregator。当HttpObjectAggregator发现是LastHttpContent,则代表http协议解析完成,封装FullHttpRequest。
  •    3:对于body内容的读取涉及到Content-Length和trunked两种方式。两种方式只是在解析协议时处理的不一致,最终输出是一致的。

2 response的流程处理

2.1实现

   只需要在netty的pipeLine中配置HttpResponseEncoder 

2.2原理

  •      1:输入是FullHttpResponse对象,输出是ByteBuf。socket再将ByteBuf数据发送到访问端。
  •      2:对FullHttpResponse按照http协议进行序列化。判断header里面是ContentLength还是Trunked,然后body按照相应的协议进行序列化。 
  •      3:具体原理和request请求方式比较类似,这次不再详细描述。  

3 netty http服务器代码展示

3.1 server

package com.suirui.http;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 * Created by zongx on 2019/12/26.
 */
public class HttpServer {

    // 工作线程池,http消息处理都由这个线程池进行
    ExecutorService httpExecutor = Executors.newFixedThreadPool(8);

    public static void main(String[] args) {
        int port ;
        if (args != null && args.length > 0) {
            port = Integer.valueOf(args[0]);
        } else {
            port = 8090;
        }
        System.out.println(port);
        new HttpServer().bind(port);
    }

    public void bind(int port) {
        //配置服务端的NIO线程组
        //两个线程组的原因是:
        // 一个用于服务端接收客户端连接
        //一个用于进行socketChannel网络读写
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        //ServerBootstrap 是netty的辅助启动类
        ServerBootstrap b = new ServerBootstrap();
        // .group传入两个线程组
        // .channel设置创建的Channel类型为NioServerSocketChannel
        // .option 配置NioServerSocketChannel的TCP参数
        // .childHandler绑定IO事件的处理类 类似reactor模式中的handler:进行连接与读写socket。
        // ChildChannelHandler会重写initChannel,保证当创建NioServerSocketChannel成功之后,再进行初始化。
        /*************.handler 与   .childHandler的区别*******/
        // handler()和childHandler()的主要区别是,handler()是发生在初始化的时候,childHandler()是发生在客户端连接之后。
        //ChannelOption.SO_BACKLOG是输入连接指示(对连接的请求)的最大队列长度被设置为 backlog 参数。如果队列满时收到连接指示,则拒绝该连接。
        b.group(bossGroup,workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChildChannelHandler());
        //绑定端口,等待成功
        try {
            //.sync()是同步阻塞接口,等待绑定操作完成
            //ChannelFuture主要用于异步操作的通知回调
            ChannelFuture f = b.bind(port).sync();
            //等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            //释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }


    }

    private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
        //注意此处不要使用ServerSocketChannel,否则客户端无法启动端口
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            //继承了ChannelHandlerAppender,并且创建了一个HttpRequestDecode和一个HttpResponseEncoder
            ch.pipeline().addLast(new HttpServerCodec());

            // 目的是将多个消息转换为单一的request或者response对象,参数为聚合后http请求的大小线程
            ch.pipeline().addLast(new HttpObjectAggregator(64 * 1024));

            //目的是支持异步大文件传输()
            ch.pipeline().addLast(new ChunkedWriteHandler());

            //业务逻辑
            ch.pipeline().addLast(new HttpServerRequestHandler(httpExecutor,"/http/"));

        }
    }
}

3.2 业务逻辑handler

package com.suirui.http;

import com.alibaba.fastjson.JSONObject;
import com.suirui.http.controller.HttpHandlerThread;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import org.apache.log4j.Logger;

import java.util.concurrent.ExecutorService;

/**
 * Created by zongx on 2019/12/31.
 */
public class HttpServerRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    private static Logger logger = Logger.getLogger(HttpServerRequestHandler.class);

    private final String httpPrefix;

    private ExecutorService httpExecutors;

    public HttpServerRequestHandler(ExecutorService httpExecutors, String httpPrefix) {
        this.httpPrefix = httpPrefix;
        this.httpExecutors = httpExecutors;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        String uri = msg.getUri();
        //判断前端来的url路径是否正确
        if (uri.startsWith(httpPrefix)) {
            JSONObject json = new JSONObject();
            json.put("code", 200);
            json.put("message", "操作成功");

            //获取要进行的操作与json参数
            int index = uri.indexOf("?");
            String op = uri.substring(httpPrefix.length(), index == -1 ? uri.length() : index);
            String param = (index == -1) ? "" : uri.substring(index + 1);
            String body = "";

            //获取请求体
            int len = msg.content().readableBytes();
            if (len > 0) {
                byte[] content = new byte[len];
                msg.content().readBytes(content);
                body = new String(content, "UTF-8");
            }

            //通过多线程线程池将处理结果返还
            HttpHandlerThread t = new HttpHandlerThread(
                    msg.getProtocolVersion(),
                    HttpHeaders.isKeepAlive(msg),
                    ctx.channel(),
                    param,
                    body,
                    op);
            httpExecutors.execute(t);

        }

    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值