2021-08-25

Java原生API之痛
1.JAVA NIO 和 JAVA AIO封装程度底,用来实现网络服务器难度高
2.JAVA NIO 和 JAVA AIO没有提供断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流等的处理,需要开发者自己解决。
Netty的优势
1.Netty提供的异步,事件驱动的网络应用程序框架和工具,用以快速开发高性能,高可靠性的网络服务。
2.丰富的协议(TCP,UDP,HTTP,WebSocket,SSL/TCS等),因此Netty开发者能够在不失灵活的前提下来实现开发的简易性,高性能和稳定性。
异步和事件驱动
Netty的IO都是异步的,所有的IO调用会立即返回,返回ChannelFuture,Netty可以通过ChannelFuture通知调用是否成功或失败。
列如:
serverBootstrap.bind(port).addListener(future -> {if(future.isSuccess()){
System.out.println(“端口绑定成功!”);
}else {
System.out.println(“端口绑定失败!”);
}
});

高效的网络传输Java 原生的序列化主要存在以下几个弊端:

* 无法跨语言。

* 序列化后码流太大。

* 序列化后性能太低。

业界有非常多的框架用于解决上述问题,如 Google Protobuf、JBoss Marshalling、Facebook Thrift等。针对这些框架,Netty 都提供了相应的包将这些框架集成到应用中。

Netty 架构图
![在这里插入图片描述](https://img-blog.csdnimg.cn/8f23f2909c414a188fba898b89f823aa.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVp在这里插入图片描述
从上述的架构图可以看出,Netty 主要由三大块组成:
*
核心组件(事件模型、字节缓冲区和通信API)
*
传输服务
*
协议

事件:入站事件与出战事件(通过数据流的流向区分)
入站事件:
*
连接已被激活或者连接失活
*
数据读取。
*
用户事件。
*
错误事件。

出站事件:
*
打开或者关闭到远程节点的连接。
*
将数据写到或者冲刷到套接字。
Netty 组件
1.channel
2.EventLoop:在网络连接的整个生命周期中。所有事件的处理主要是有EventLoop处理。
3.ChannelFutrue:在 Netty 中,I/O 操作主要都是异步进行,当操作发生时,我们需要通过一种方式来知道操作在未来的时间点的执行结果。ChannelFutrue 中的 addListener () 方法,可以注册监听器 ChannelFutureListener,当操作完成时,监听器可以主动通知我们。
4.ChannelHander:主要用于应用程序中业务逻辑的处理,网络中进入与出去的数据都由它处理,当有事件发生时,channelHandler会被触发。
5.ChannelPipeline:提供了一种容器,用于定义数据流入与流出过程中的处理流程,进行数据的处理与加工。
6.ByteBuf:字节数据容器,提供比Java NIO ByteBuffer更好的API。

附代码:
package com.zing.Client;

import com.zing.hander.NettyClientHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**

  • @author zengling

  • @create 2021-08-25-16:34
    */
    public class ClientOne {

    public static void main(String[] args) throws Exception {
    //客户端需要一个事件循环组
    EventLoopGroup group = new NioEventLoopGroup();
    try {
    //创建客户端启动对象
    //注意客户端使用的不是 ServerBootstrap 而是 Bootstrap
    Bootstrap bootstrap = new Bootstrap();
    //设置相关参数
    bootstrap.group(group) //设置线程组
    .channel(NioSocketChannel.class) // 设置客户端通道的实现类(反射)
    .handler(new ChannelInitializer() {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
    ch.pipeline().addLast(new NettyClientHandler()); //加入自己的处理器
    }
    });
    System.out.println(“客户端 ok…”);
    //启动客户端去连接服务器端
    //关于 ChannelFuture 要分析,涉及到netty的异步模型
    ChannelFuture channelFuture = bootstrap.connect(“127.0.0.1”, 7788).sync();
    //给关闭通道进行监听
    channelFuture.channel().closeFuture().sync();
    } finally {
    group.shutdownGracefully();
    }
    }
    }

package com.zing.hander;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**

  • @author zengling

  • @create 2021-08-25-16:38
    */
    public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    //当通道就绪就会触发该方法
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println(“client ctx =” + ctx);
    ctx.writeAndFlush(Unpooled.copiedBuffer(“老板,工资什么时候发给我啊?”, CharsetUtil.UTF_8));
    }

    //当通道有读取事件时,会触发
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    ByteBuf buf = (ByteBuf) msg;
    System.out.println(“服务器回复的消息:” + buf.toString(CharsetUtil.UTF_8));
    System.out.println("服务器的地址: "+ ctx.channel().remoteAddress());
    }

    //处理异常, 一般是需要关闭通道
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
    ctx.close();
    }
    }

package com.zing.Server;

import com.zing.hander.NettyServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**

  • @author zengling

  • @create 2021-08-25-16:34
    */
    public class ServerOne {

     public static void main(String[] args) throws Exception {
         //创建BossGroup 和 WorkerGroup
         //说明
         //1. 创建两个线程组 bossGroup 和 workerGroup
         //2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理,会交给 workerGroup完成
         //3. 两个都是无限循环
         //4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
         //   默认实际 cpu核数 * 2
         //
         EventLoopGroup bossGroup = new NioEventLoopGroup(1);
         EventLoopGroup workerGroup = new NioEventLoopGroup(); //8
         try {
             //创建服务器端的启动对象,配置参数
             ServerBootstrap bootstrap = new ServerBootstrap();
             //使用链式编程来进行设置
             bootstrap.group(bossGroup, workerGroup) //设置两个线程组
                     .channel(NioServerSocketChannel.class) //bossGroup使用NioSocketChannel 作为服务器的通道实现
                     .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接个数 option主要是针对boss线程组,
                     .childOption(ChannelOption.SO_KEEPALIVE, true) //设置保持活动连接状态 child主要是针对worker线程组
                     .childHandler(new ChannelInitializer<SocketChannel>() {//workerGroup使用 SocketChannel创建一个通道初始化对象																														(匿名对象)
                         //给pipeline 设置处理器
                         @Override
                         protected void initChannel(SocketChannel ch) throws Exception {
                             //可以使用一个集合管理 SocketChannel, 再推送消息时,可以将业务加入到各个channel 对应的 NIOEventLoop 的 									taskQueue 或者 scheduleTaskQueue
                             ch.pipeline().addLast(new NettyServerHandler());
                         }
                     }); // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器
    
             System.out.println(".....服务器 is ready...");
             //绑定一个端口并且同步, 生成了一个 ChannelFuture 对象
             //启动服务器(并绑定端口)
             ChannelFuture cf = bootstrap.bind(7788).sync();
             //给cf 注册监听器,监控我们关心的事件
             cf.addListener(new ChannelFutureListener() {
                 @Override
                 public void operationComplete(ChannelFuture future) throws Exception {
                     if (cf.isSuccess()) {
                         System.out.println("服务已启动,端口号为7788...");
                     } else {
                         System.out.println("服务启动失败...");
                     }
                 }
             });
             //对关闭通道进行监听
             cf.channel().closeFuture().sync();
         } finally {
             bossGroup.shutdownGracefully();
             workerGroup.shutdownGracefully();
         }
     }
    

}
package com.zing.hander;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**

  • @author zengling

  • @create 2021-08-25-16:39
    */
    public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    //读取数据实际(这里我们可以读取客户端发送的消息)
    /*

    1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址
    2. Object msg: 就是客户端发送的数据 默认Object
      */
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
      System.out.println(“server ctx =” + ctx);
      Channel channel = ctx.channel();
      //将 msg 转成一个 ByteBuf
      //ByteBuf 是 Netty 提供的,不是 NIO 的 ByteBuffer.
      ByteBuf buf = (ByteBuf) msg;
      System.out.println(“客户端发送消息是:” + buf.toString(CharsetUtil.UTF_8));
      System.out.println(“客户端地址:” + channel.remoteAddress());
      }

    //数据读取完毕
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    //writeAndFlush 是 write + flush
    //将数据写入到缓存,并刷新
    //一般讲,我们对这个发送的数据进行编码
    ctx.writeAndFlush(Unpooled.copiedBuffer(“公司最近账户没啥钱,再等几天吧!”, CharsetUtil.UTF_8));
    }

    //处理异常, 一般是需要关闭通道
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    ctx.close();
    }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值