4.通信层

来源:互联网 发布:windows phone 8.1 编辑:程序博客网 时间:2024/05/30 04:27
  1. 概述
    1. rocketmq通信层采用netty来实现,netty是nio socket的一个框架
  2. nio vs io
    1. 区别

      ionionio优势nio缺点面向流面向缓冲面向缓冲区的编程方式流式处理数据直观,优雅,nio不具备这些优点阻塞非阻塞数据不可获取时不进行等待,节省线程获取数据可能只获取到一部分,达不到处理标准,编程不直观


  3. nio socket vs io
    1. 阻塞io:适用于连接少,发送大量数据

    2. 非阻塞io:适用于连接多,发送少量数据

  4. nio selector
    1. 用于监听多个通道的事件,比如连接打开,数据到达等

    2. 一个线程即可监听各个通道的状态,然后再结合nio的非阻塞特性,可以大大减少服务器的线程资源消耗。
  5. rocketmq中netty的使用与参数设置
    1. broker端 com.alibaba.rocketmq.remoting.netty.NettyRemotingServer.start

      ServerBootstrap childHandler = this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupWorker)//前者为1个线程的线程池,用于处理客户端连接建立事件,后者为3个线程的线程池,用于处理已经建立连接的io事件    .channel(NioServerSocketChannel.class)//为客户端连接事件创建连接    .option(ChannelOption.SO_BACKLOG, 1024)//最大连接数 参见java.net.ServerSocket.ServerSocket(int port, int backlog)    .option(ChannelOption.SO_REUSEADDR, true)//之前的连接处于timeout状态,新可以使用该地址 参见java.net.ServerSocket.setReuseAddress(boolean on)    .option(ChannelOption.SO_KEEPALIVE, false)//不开启长连,参见java.net.SocketOptions.SO_KEEPALIVE    .childOption(ChannelOption.TCP_NODELAY, true)//对此连接禁用 Nagle算法,参见java.net.SocketOptions.TCP_NODELAY     .option(ChannelOption.SO_SNDBUF, nettyServerConfig.getServerSocketSndBufSize())//网络发送基础缓冲区建议大小,值为65535,参见java.net.SocketOptions.SO_SNDBUF     .option(ChannelOption.SO_RCVBUF, nettyServerConfig.getServerSocketRcvBufSize())//网络接收基础缓冲区建议大小,值为65535,参见java.net.SocketOptions.SO_RCVBUF    .localAddress(new InetSocketAddress(this.nettyServerConfig.getListenPort()))//设置监听端口    .childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {    ch.pipeline().addLast(//为当前channel的pipeline添加自定义处理器defaultEventExecutorGroup, //默认为8个线程的线程池new NettyEncoder(), //输出数据时进行编码new NettyDecoder(), //输入数据时进行解码new IdleStateHandler(0, 0, nettyServerConfig    .getServerChannelMaxIdleTimeSeconds()),//默认120秒检测一次连接是否空闲new NettyConnetManageHandler(), // 处理连接事件,建立,断开等等new NettyServerHandler()); //处理请求,根据请求码(com.alibaba.rocketmq.common.protocol.RequestCode)来查找相应的处理器,处理请求,处理器注册参考类com.alibaba.rocketmq.broker.BrokerController.registerProcessor}    });try {    ChannelFuture sync = this.serverBootstrap.bind().sync();//创建channel,绑定端口,等待返回    InetSocketAddress addr = (InetSocketAddress) sync.channel().localAddress();    this.port = addr.getPort();}catch (InterruptedException e1) {    throw new RuntimeException("this.serverBootstrap.bind().sync() InterruptedException", e1);}

      几个tcp参数释义:http://www.open-open.com/lib/view/open1412994697952.html

    2. client端 com.alibaba.rocketmq.remoting.netty.NettyRemotingClient.start

      Bootstrap handler = this.bootstrap.group(this.eventLoopGroupWorker)//默认为1个线程的线程池,处理channel中的io事件    .channel(NioSocketChannel.class)//与ServerBootstrap一致    .option(ChannelOption.TCP_NODELAY, true)//与ServerBootstrap一致    .option(ChannelOption.SO_KEEPALIVE, false)//与ServerBootstrap一致    .option(ChannelOption.SO_SNDBUF, nettyClientConfig.getClientSocketSndBufSize())//与ServerBootstrap一致    .option(ChannelOption.SO_RCVBUF, nettyClientConfig.getClientSocketRcvBufSize())//与ServerBootstrap一致    .handler(new ChannelInitializer<SocketChannel>() {public void initChannel(SocketChannel ch) throws Exception {    ch.pipeline().addLast(//defaultEventExecutorGroup, //默认为4个线程的线程池new NettyEncoder(), //与ServerBootstrap一致new NettyDecoder(), //与ServerBootstrap一致new IdleStateHandler(0, 0, nettyClientConfig.getClientChannelMaxIdleTimeSeconds()),//与ServerBootstrap一致new NettyConnetManageHandler(), //与ServerBootstrap类似new NettyClientHandler());//处理请求,根据请求码(com.alibaba.rocketmq.common.protocol.RequestCode)来查找相应的处理器,处理请求,处理器注册参考类com.alibaba.rocketmq.client.impl.MQClientAPIImpl的初始化方法,处理器为com.alibaba.rocketmq.client.impl.ClientRemotingProcessor}    });


    3. com.alibaba.rocketmq.remoting.netty.NettyDecoder继承自LengthFieldBasedFrameDecoder
      即根据长度来解析数据,此类关键的几个属性如下:

      int maxFrameLength//最大数据长度,超出抛异常,默认为8M
      int lengthFieldOffset//数据长度起始偏移量,默认为0
      int lengthFieldLength//数据长度长度,默认为4
      int lengthAdjustment//数据长度调节量,默认为0
      int initialBytesToStrip//数据解码时移除的字节量,默认为4

      示例如下:         原始数据                      解码后            +                            |            |                            |+-----------v+--------------+     +------v-------+|            |              |     |              || 0x0000000C | HELLO, WORLD +-----> HELLO, WORLD ||            |              |     |              |+------------+--------------+     +--------------+0x0000000C占4个字节,十进制为12解码过程如下:1 根据lengthFieldOffset,从第1个字节开始读取2 根据lengthFieldLength,读取4个字节,此时知道数据的长度为12个字节,但是不知道该数据是否包含的包头的长度3 根据lengthAdjustment,可知长度不用调整,就是12个字节都是后面数据的长度4 根据initialBytesToStrip,需要将前面的4个字节舍去,即只剩12个字节 再举个例子说明一下:lengthFieldOffset   = 1lengthFieldLength   = 2  lengthAdjustment    = 1  initialBytesToStrip = 3BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)  +------+--------+------+----------------+      +------+----------------+  | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |  | 0xCA | 0x000C | 0xFE | HELLO, WORLD   |      | 0xFE | HELLO, WORLD   |  +------+--------+------+----------------+      +------+----------------+  1 根据lengthFieldOffset,从第二个字节开始读取2 根据lengthFieldLength,读取两个字节,此时知道数据的长度为12,但是并不知道数据是否包含包头的长度3 根据lengthAdjustment,可知长度需要调整,即12+1为13,数据长度调整为134 根据initialBytesToStrip,需要将前面的3个字节舍去,即只剩 HDR2+实际内容的长度


  6. rocketmq中数据的传输方式在netty中的实现:参见com.alibaba.rocketmq.remoting.netty.NettyRemotingAbstract
    1. netty中传输数据:

      channel.writeAndFlush(request).addListener(new ChannelFutureListener() {//listener在数据返回后执行,不会阻塞当前线程public void operationComplete(ChannelFuture f) throws Exception {   //处理返回的结果}});


    2. 同步

      channel.writeAndFlush(request).addListener(new ChannelFutureListener() {//listener在数据返回后执行,不会阻塞当前线程public void operationComplete(ChannelFuture f) throws Exception {   //处理返回的结果   countDownLatch.countDown();}});countDownLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);


    3. 异步

      boolean acquired = Semaphore.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS)//使用信号量限制异步方式的调用,实现超时机制和防止netty缓存请求过多channel.writeAndFlush(request).addListener(new ChannelFutureListener() {//listener在数据返回后执行,不会阻塞当前线程public void operationComplete(ChannelFuture f) throws Exception {   //处理返回的结果   InvokeCallback.operationComplete(ResponseFuture responseFuture)   Semaphore.release();//信号量释放}});


    4. oneway

      boolean acquired = Semaphore.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS)//使用信号量限制oneway方式的调用,实现超时机制和防止netty缓存请求过多if(acquired){channel.writeAndFlush(request).addListener(new ChannelFutureListener() {//listener在数据返回后执行,不会阻塞当前线程public void operationComplete(ChannelFuture f) throws Exception {    //处理返回的结果        Semaphore.release();//信号量释放}    });}


0 0
原创粉丝点击