7.Netty

Netty
    介绍:是一个NIO client-server(客户端服务器)框架,使用Netty可以快速开发网络应用,它提供了
           简单易用的api从网络处理代码中解耦业务逻辑。Netty完全基于NIO实现的,Netty是异步的。
    
    Netty健壮性、功能、性能、可定制性和可扩展性在同类框架都是首屈一指的。
    
    网站:http://ifeve.com/netty5-user-guide/
    
    Netty实现通信的步骤:
        1:创建两个NIO线程组,一个用于网络事件处理(接受客户端的连接),另一个则进行网络
          通信的读写。
        2:创建一个ServerBootstarp对象,配置Netty的一系列参数,如传出去数据的缓存
        3:创建一个实际处理数据的类ChannelInitializer,进行初始化的准备工作,比如设置接受传出
          数据的字符集、格式、已经实际处理数据的接口
        4:绑定端口,执行同步阻塞方法等待服务器端口启动即可
    
    Netty对Byte实现了优化,因为有两个指针
    
    TCP粘包、拆包问题
        1、应用程序write写入的字节大小大于套接口发送缓冲区的大小
        2、进行MSS大小的TCP分段
        3、以太网帧的payload大于MTU进行IP分片
        
    TCP粘包、拆包解决方案
        1、消息定长
        2、在包尾部增加特殊字符进行分割,例如回车
        3、将消息分为消息头和消息体,在消息头中包含表示消息总总长度的字段,然后进行业务处理
    
    Hetty解决粘包、拆包问题
        1、分隔符 DelimiterBasedFrameDecoder
            在Server类和Client类中initChannel方法中
                ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
                sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
                //设置字符串形式的解码
                sc.pipeline().addLast(new StringDecoder());
                
                sc.pipeline().addLast(new ServerHandler());
                
            Server类
                public class Server {
                    public static void main(String[] args) throws InterruptedException {
                        //第一个线程组 是用于接收Client端连接
                        EventLoopGroup bossGroup = new NioEventLoopGroup();
                        //第二个线程组 适用于实际的业务处理操作的
                        EventLoopGroup workerGroup = new NioEventLoopGroup();
                        
                        //创建一个辅助类Bootstrap,就是对我们Server进行一系列的配置
                        ServerBootstrap b = new ServerBootstrap(); // (2)
                        //把两个工作线程组加入进来
                         b.group(bossGroup, workerGroup)
                         //我要指定使用NioserverSocketChannel这种类型的通道
                          .channel(NioServerSocketChannel.class)
                          //一定要使用childHandler去绑定具体的事件处理器
                          .childHandler(new ChannelInitializer<SocketChannel>() {
                              @Override
                            protected void initChannel(SocketChannel sc) throws Exception {
                                // TODO Auto-generated method stub
                                ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
                                sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
                                sc.pipeline().addLast(new StringDecoder());
                                sc.pipeline().addLast(new ServerHandler());
                            }
                        });
                        
                         ChannelFuture f = b.bind(8765).sync();
                        
                         f.channel().closeFuture().sync();
                        
                         bossGroup.shutdownGracefully();
                         workerGroup.shutdownGracefully();
                    }
                }
            
            ServerHandler类
                public class ServerHandler extends ChannelHandlerAdapter{
                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                            String request = (String)msg;
                            System.out.println("Server:"+request);
                            //写给客户端
                            String response = "我是反馈的信息$_";
                            ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()))
                                .addListener(ChannelFutureListener.CLOSE);
                    }
                    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                        // TODO Auto-generated method stub
                        cause.printStackTrace();
                        ctx.close();
                    }
                }
            
            Client类
                public class Client {
                    public static void main(String[] args) throws Exception{
                        EventLoopGroup group = new NioEventLoopGroup();

                        Bootstrap b = new Bootstrap();
                        b.group(group)
                        .channel(NioSocketChannel.class)
                        .handler(new ChannelInitializer<SocketChannel>(){
                            @Override
                            protected void initChannel(SocketChannel sc) throws Exception {
                                // TODO Auto-generated method stub
                                ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
                                sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
                                sc.pipeline().addLast(new StringDecoder());
                                sc.pipeline().addLast(new ClientHandler());
                            }
                        });

                        ChannelFuture cf1 = b.connect("127.0.0.1",8765).sync();

                        cf1.channel().writeAndFlush(Unpooled.wrappedBuffer("hello netty!$_".getBytes()));
                        cf1.channel().writeAndFlush(Unpooled.wrappedBuffer("hello netty!$_".getBytes()));

                        cf1.channel().closeFuture().sync();
                        group.shutdownGracefully();
                    }
                }
            
            ClientHandler类
                public class ClientHandler extends ChannelHandlerAdapter{
                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                        try {
                            //do something msg
                            String data = (String)msg;
                            System.out.println("Client: " + data);
                            
                        } finally {
                            ReferenceCountUtil.release(msg);
                        }
                        
                    }
                    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                        // TODO Auto-generated method stub
                        cause.printStackTrace();
                        ctx.close();
                    }
                }
                
        2、FixedLengthFrameDecoder(定长)
            在Server类和Client类中initChannel方法中
                //设置定长字符串接收
                sc.pipeline().addLast(new FixedLengthFrameDecoder(5));
                //设置字符串的解码
                sc.pipeline().addLast(new StringDecoder());
                sc.pipeline().addLast(new ServerHandler());
    
Netty编解码技术
    编解码技术就是java序列化技术,目的:第一进行网络传输,第二对象持久化
    虽然我们可以使用java进行对象序列化,netty去传输,但是java序列化的硬伤太多,比如
        java序列化没法跨语言、序列化后码流太大、序列化性能太低等等
        
    主流框架:
        JBoss的Marshalling包
        goole的Protobuf
        基于Protobuf的Kyro
        MessagePack框架
        
    JBoss的Marshalling是一个java对象序列化包,对JDK默认的序列化框架进行了优化,但又保持
        跟java.io.Serializable接口的兼容,同时增加了一些可调的参数和附加特性
        
        在Server类和Client类中initChannel方法中
        sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
        sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
        sc.pipeline().addLast(new ReadTimeoutHandler(5));
        sc.pipeline().addLast(new ServerHandler());
        
        ServerHandler中
        Req req = (Req) msg;
        System.out.println("server:"+req.getId()+","+req.getName()+","+req.getRequestMessage());
        
        Resp resp = new Resp();
        resp.setId(req.getId());
        resp.setName(req.getName());
        resp.setResponseMessage(req.getRequestMessage());
        ctx.writeAndFlush(resp);
        
        MarshallingCodeCFactory类
        public final class MarshallingCodeCFactory {
            /**
             * 创建Jboss Marshalling解码器MarshallingDecoder
             * @return MarshallingDecoder
             */
            public static MarshallingDecoder buildMarshallingDecoder() {
                //首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
                final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
                //创建了MarshallingConfiguration对象,配置了版本号为5
                final MarshallingConfiguration configuration = new MarshallingConfiguration();
                configuration.setVersion(5);
                //根据marshallerFactory和configuration创建provider
                UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
                //构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
                MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024);
                return decoder;
            }

            /**
             * 创建Jboss Marshalling编码器MarshallingEncoder
             * @return MarshallingEncoder
             */
            public static MarshallingEncoder buildMarshallingEncoder() {
                final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
                final MarshallingConfiguration configuration = new MarshallingConfiguration();
                configuration.setVersion(5);
                MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
                //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
                MarshallingEncoder encoder = new MarshallingEncoder(provider);
                return encoder;
            }
        }
        
webSocket将网络套接字引入到了客户端和服务端,
    ws特点:
        单一的tcp连接,双方可通信
        对代理、防火墙和路由器透明
        无头部信息、Cookie和身份验证
        无安全开销
        通过ping\pong帧保持链路激活
        服务器可主动传递给客户端,不在需要客户端轮询

实践
    两台机器或者多台使用Netty进行通信
        第一种,使用长连接通道不断开的形式进行通信,服务器和客户端通道一直处于开启状态
        第二种,一次性批量提交数据,采用短连接方式
        第三种,使用特殊的长连接,在指定的时间内,没有通信就断开,下次通信在建立连接
            如何设置超时后关闭通道,关闭后又如何再次建立连接
                5秒钟关闭,server和Client都要设置
                sc.pipeline().addLast.(new ReadTimeoutHandler(5));
                关闭之后,Client开启一个线程去建立连接
                new Thread(new Runnable(){
                    public void run() {
                        try {
                            System.out.println("进入子线程");
                            ChannelFuture cf = c.getChannelFuture();//一个连接服务器方法
                            System.out.println(cf.channel().isActive());
                            System.out.println(cf.channel().isOpen());
                            //再次发送数据
                            Req request = new Req();
                            request.setId(""+4);
                            request.setName("pro"+4);
                            request.setRequestMessage("数据信息"+4);
                            cf.channel().writeAndFlush(request);
                            cf.channel().closeFuture().sync();
                            System.out.println("子线程结束");
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
                
                
            客户端宕机时关闭通道,下次客户端重启之后再次建立连接,服务器宕机重启后,如何建立连接
    
    心跳检测
    
    Netty实现文件服务器上传下载
        相比Struts或springmvc,Netty上传下载支持大文件,异步,并发,性能好,非阻塞
        
    fastdfs支持小文件上传,hdfs支持大文件、断点续传
        
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值