Java netty框架学习笔记

netty介绍

  • 1、netty是由Jboss提供的一个Java开源框架,现在为github上的独立项目。
  • 2、netty是一个异步的、基于事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络IO程序。
  • 3、netty主要针对在tcp协议下,面向clients端的高并发应用,或者peer-to-peer场景下的大量数据持续传输的应用。
  • 4、netty本质是一个NIO框架,适用于服务器通讯相关的多种应用场景。
  • 5、要透彻理解netty,需要先学习nio,这样才能阅读netty的源码。

netty的应用场景

  • 1、互联网行业:在分布式系统中,各个节点之间需要远程服务调用,高性能的RPC框架必不可少,Netty作为异步高性能的通信框架,往往作为基础的通信之间被这些RPC框架使用。
  • 2、典型的应用:阿里分布式服务框架Dubbo的RPC框架使用Dubbo协议进行节点间通信,Dubbo协议默认使用netty作为基础通信组件,用于实现各进程节点之间的内部通信。
  • 3、游戏行业:无论是手游还是大型的网络游戏,netty作为高性能的基础通信组件,提供了TCP/UDP和HTTP协议栈,方便定制和开发私有协议栈,账号登陆服务器。
  • 4、地图服务器之间可以方便的通过netty进行高性能的通信。
  • 5、大数据领域,经典的Hadoop的高性能通信和序列化组件(AVRO是西安数据文件共享)的RPC框架,默认采用netty进行跨界点通信。其Netty Service是基于netty进行的二次封装。

IO模型

  • IO模型基本说明
    • 1、IO模型简单的理解:就是用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的性能
    • 2、JAVA共支持3种网络编程模型:BIO、NIO、AIO
    • 3、JavaBIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。
    • 4、JavaNIO:同步非阻塞,服务器实现模式为一个线程处理多个请求,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求就进行处理。
    • 5、JavaAIO(NIO.2):异步非阻塞,AIO引入了异步通道的概念,采用了Proactor模式,简化了程序编写,有效的请求才启动线程,它的特点是由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。
  • BIO、NIO、AIO适用场景
    • 1、BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用种,JDK1.4之前的唯一选择,但程序简单易理解。
    • 2、NIO方式适用于连接数目多且连接比较短的架构,比如聊天服务器、弹幕系统,服务器间通讯等。变成比较复杂 ,java1.4开始支持。
    • 3、AIO方式适用于连接数目多且连接比较长的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,jdk7开始支持。

BIO code

public class BioTest {

    public static void main(String[] args) throws Exception {
        //创建一个线程池
        //如果有客户端连接,就创建一个新城,与之进行通讯

        ExecutorService pool = Executors.newCachedThreadPool();

        ServerSocket serverSocket = new ServerSocket(6666);

        System.out.println("服务器已启动。。。。。。。");

        while(true){
            //监听、等待客户端连接
            Socket socket = serverSocket.accept();
            System.out.println("连接到一个客户端。。。。。。");

            pool.execute(new Runnable() {
                @Override
                public void run() {
                    BioTest.handler(socket);
                }
            });
        }
    }

    //编写一个handler方法,和客户端通讯
    public static void handler(Socket socket){
        try {
            byte[] buffer = new byte[1024];
            InputStream is = socket.getInputStream();
            int len;
            while((len = is.read(buffer)) != -1){
                System.out.println(new String(buffer, 0, len));
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("关闭和client的连接");
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

为了使BIO能处理多个客户端的连接,使用ThreadPool进行了升级,具体的使用方式见上述代码。
代码中的accept和read方法,都会出现阻塞的情况。

NIO三大组件:

  • 1、channel
  • 2、buffer
  • 3、selector

NIO三大组件示意图

NIO三大核心示意图

  • 1、每个channel都会对应一个buffer
  • 2、Selector对应一个线程,一个线程对应多个channel
  • 3、程序切换到哪个channel是由事件决定的,Event就是一个重要的概念
  • 4、selector会根据不同的事件,在各个通道上进行切换
  • 5、buffer就是一个内存块
  • 6、数据的读取和写入是通过buffer来进行的,这和bio是不同的,bio中要么是输入流,要么是输出流,不能是双向的,但是nio的buffer是既可以读也可以写的使用flip进行切换
  • 7、channel是双向的,可以返回底层操作系统的情况,比如linux,底层的操作系统通过就是双向的

缓冲区buffer

  • 基本介绍
    缓冲区本质上是一个可以读写数据的内存块,可以理解成一个容器对象(含有一个数组),该对象提供了一组方法,可以更轻松地使用内存块,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。Channel提供从文件、网络读取数据的渠道,但是读取或者写入的数据都必须经过buffer
  • 常用buffer子类:
    • 1、ByteBuffer
    • 2、ShortBuffer
    • 3、CharBuffer
    • 4、IntBuffer
    • 5、LongBuffer
    • 6、DoubleBuffer
    • 7、FloatBuffer
  • Buffer类主要属性:
    • 1、Capacity:可以容纳的最大数量,在缓冲区创建时被设定并且不能修改
    • 2、Limit:表示缓冲区的当前终点,不能对缓冲区超过极限的位置进行读写操作,极限可修改
    • 3、Position:位置,下一个要被读写的元素的索引,每次读写缓冲区数据时都会改变,为下次读写做准备
    • 4、Mark:标记

通道Channel

  • NIO的Channel类似于流,但是有如下区别:
    • 1、Channel可以同时进行读和写,但是流只能读,或者只能写
    • 2、Channel可以实现异步读写数据
    • 3、Channel可以从缓冲区读数据,也可以写数据到缓冲区

选择器 Selector

  • 基本介绍
    • 1、 Java的NIO,用非阻塞的IO方式,可以用一个线程,处理多个的客户端连接,就会使用到selector
    • 2、selector能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的处理。这样就可以只用一个线程去管理多个通道,也就是管理多个连接和请求。
    • 3、只有在连接/通道上真正有读写事件发生的时候,才会进行读写,就大大减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程。
    • 4、避免了多线程之间的上下文切换导致的开销。
  • 流程
    • 1、当客户端连接时,会通过ServerSocketChannel得到SocketChannel。
    • 2、将SocketChannel注册到Selector上,一个selector可以注册多个socketChannel。
    • 3、注册后返回一个SelectionKey,会和该Selector关联。
    • 4、Selector进行监听,select方法,返回有事件发生的通道的个数。
    • 5、进一步得到各个SelectionKey。
    • 6、再通过SelectionKey反向获取到SocketChannel。
    • 7、可以通过得到的socketChannel完成业务处理。
  • code
    @Test
    public void test_selectorServer() throws Exception {
        //获取selector
        Selector selector = Selector.open();
    
        //获取serverSocketChannel,并绑定端口
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.bind(new InetSocketAddress(6666));
    
        //设置为非阻塞
        serverChannel.configureBlocking(false);
    
        //把serverSocketChannel注册到selector中,关心事件为SelectionKey.OP_ACCEPT
        SelectionKey key = serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    
        //循环等待客户端连接
        while(true){
            //等待1秒,如果没有事件发生,则进行返回
            if(selector.select(1000) == 0){
                System.out.println("服务器等待了1秒,无连接。。。。。。");
                continue;
            }
            //如果返回的不是0,表示已经获取到关注的事件了
            //selector.selectedKeys()返回关注事件的集合,可以通过SelectionKey反向获取通道
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = keys.iterator();
            while(keyIterator.hasNext()){
                //获取SelectionKey
                SelectionKey selectionKey = keyIterator.next();
                //根据key对应的通道发生的事件,做相应的处理
                if(selectionKey.isAcceptable()){//如果是accept的事件
                    //给该客户端生成一个socketChannel
                    SocketChannel socketChannel = serverChannel.accept();
                    //将socketChannel设置为非阻塞
                    socketChannel.configureBlocking(false);
                    //将socketChannel注册进入selector中,关注事件为OP_READ,同时为该channel关联一个buffer
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }
                if(selectionKey.isReadable()){//如果是OP_READ事件
                    //通过key反向获取到对应的channel
                    SocketChannel channel = (SocketChannel) selectionKey.channel();
                    //获取到该channel关联的buffer
                    ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
                    //进行读取
                    while(channel.read(buffer) != 0){
                        buffer.flip();
                        System.out.println("from 客户端:" + new String(buffer.array(), 0, buffer.limit()));
                        buffer.clear();
                    }
                }
    
                //手动从集合中移除当前的selectionKey
                keyIterator.remove();
            }
        }
    }
    
    @Test
    public void test_selectorClient() throws Exception {
        //得到一个网络通道
        SocketChannel socketChannel = SocketChannel.open();
        //配置为非阻塞的
        socketChannel.configureBlocking(false);
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 6666);
    
        if(!socketChannel.connect(address)){
            while(!socketChannel.finishConnect()){
                System.out.println("因为连接需要事件,客户端不会阻塞。。。。。。");
            }
        }
    
        String str = "hello, 胡文超" ;
        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
    
        socketChannel.write(buffer);
    
        socketChannel.close();
    }
    

netty 概述

  • 原生NIO的问题

    • 1、NIO的类库和API比较繁杂,使用麻烦,需要熟练掌握Selector、ServerSocketChannel、SocketChannel和ByteBuffer等等。
    • 2、需要具体其它的额外技能:需要熟悉Java多线程编程,因为NIO编程中涉及到Reactor模式,你必须对多线程和网络编程非常熟悉,才能写出高质量的NIO程序。
    • 3、开发工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络堵塞和异常流的处理等等。
    • 4、JDK NIO 的bug:例如Epoll Bug,会导致Selector空轮询,最终导致CPU 100%,知道JAVA7中该问题依旧存在,没有被根本解决。
  • netty 官网描述

    • 1、Netty是由JBOSS提供的一个java开源框架,netty提供异步的,基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠性的网络IO程序。
    • 2、netty可以帮助你快速、简单的开发出一个网络应用,相当于简化和流程化了NIO的开发过程。
    • 3、netty是目前最流行的NIO框架,netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,知名的ES、dubbo框架内部都采用了netty。
  • netty的优点

    • 1、设计优雅:适用于各种传输类型的统一API阻塞和非阻塞Socket;基于灵活且可扩展的事件模型,可以清晰地分离关注点;高度可定制的线程模型-单线程,一个或多个线程池。
    • 2、使用方便:详细记录的javadoc,用户指南和示例;没有其它的依赖项,JDK5或者6就足够了。
    • 3、高性能、吞吐量更高:延迟更低;减少资源消耗;最小化不必要的内存复制。
    • 4、安全:完整的SSL/TLS和StartTLS支持。
    • 5、社区活跃、不断更新:发现的bug可以被及时修复,同时,耕读的新功能会被加入进来。
      在这里插入图片描述
  • 线程模型介绍

    • 1、不同的线程模型,对程序的性能有很大影响
    • 2、目前存在的线程模型:
      • 传统阻塞I/O服务模型
      • Reactor模式
    • 3、根据Reactor的数量和处理资源池线程的数量不同,有3种典型的实现
      • 单Reactor单线程
      • 单Reactor多线程
      • 主从Reactor 多线程
    • 4、netty线程模式(netty主要基于主从Reactor多线程模型做了一定的改进,其中主从Reactor多线程模型有多个Reactor)

。。。。。持续更新中。。。。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Zynq QSPI Flash启动是指使用Zynq芯片的QSPI闪存作为启动设备来启动系统。在这种启动方式下,Zynq芯片会从QSPI闪存中读取启动程序,然后执行该程序来启动系统。这种启动方式具有启动速度快、可靠性高等优点,因此在一些应用场合下被广泛采用。 ### 回答2: Zynq是赛灵思(Xilinx)公司推出的一种FPGA和ARM Cortex-A9双核处理器内核组成的SoC。QSPI(Quad SPI)是四线SPI模式的一种扩展,它使用四根数据线,能够提供更高的数据传输速率。QSPI Flash是一种存储器芯片,通常由Spansion、Micron、Winbond等厂商生产,它能够在低功耗的情况下提供高速度的数据存取。 在Zynq中,系统启动时会从QSPI Flash中读取启动代码,然后将代码加载到内存中执行。因此,QSPI Flash启动Zynq启动的基础。以下是关于Zynq QSPI Flash启动的步骤: 1.配置QSPI Flash:需要通过手动设置或使用软件自动化配置工具来设置QSPI Flash的模式、时序和其他参数。这些参数需要与Zynq芯片的规格书配合设置,确保QSPI Flash与芯片能够正常通信。 2.在Vivado里进行设计: 需要在Vivado中设计一个Zynq系统,包括Zynq处理器IP核和QSPI Flash IP核,并将两个IP核链接在一起,以实现启动时芯片从QSPI Flash中读取代码。 3.生成bitstream: 在完成设计后,需要使用Vivado生成Zynq系统的bitstream。bitstream是一个二进制文件,包含了Zynq芯片的配置信息,可以在生产过程中烧录到芯片上。bitstream中的配置信息包括了QSPI Flash的配置参数、启动代码的地址位置和大小等。 4.编写启动代码:需要编写适合于Zynq启动代码,该代码需要放置在QSPI Flash的制定地址位置以供Zynq进行启动启动代码可以是裸机代码或操作系统(如Linux)。 5.测试系统:当设计开发工作完成后,需要进行系统测试。通过对系统的测试,可以确保QSPI Flash启动的正确性。 在进行Zynq QSPI Flash启动设计时,需要考虑芯片与QSPI Flash之间的通信和数据传输,注重细节,确保系统的稳定性和可靠性。 ### 回答3: Zynq QSPI flash启动是指FPGA芯片通过QSPI接口连接外部闪存启动。这种启动方式具有启动速度快、布线短等优点,非常适合使用在资源受限的嵌入式系统中。 下面简要介绍Zynq QSPI flash启动的基本流程和注意事项。 1. 确定启动模式:Zynq支持两种启动模式,分别是JTAG启动和QSPI启动。在实际应用中,一般使用QSPI启动。 2. 配置引脚:根据所使用的芯片和开发板,需要确认相关的引脚连接配置。例如,需要确认QSPI数据线、时钟线、CS线等连接方式。 3. 配置闪存:在设计和布局PCB时,需要确认闪存的操作模式、地址空间、芯片选择等参数。在实际操作中,需要编写相应的启动代码和修改Vivado中的设计约束文件。 4. 切换MIO模式:为了让QSPI启动,需要切换相应引脚的MIO模式。例如,需要将SD0引脚切换为QSPI MIO模式。 5. 烧录启动镜像:制作好启动镜像后,需要使用编程器将镜像烧录到闪存中。常见的启动镜像格式包括BIF、SD等。 注意事项: 1. 保证闪存中有启动镜像。 2. 确认各个引脚的连接方式和信号电平,防止可能存在的焊接错误或短路现象。 3. 在编写启动代码和设计约束文件时,需要熟悉Zynq启动流程和寄存器配置信息,避免出现错误。 4. 选择合适的闪存芯片和布局方式,以保证启动速度和布线长度的平衡。 总之,Zynq QSPI flash启动是一种高效、便捷的启动方式,可以大大降低嵌入式系统的启动时间,提高系统性能。但操作时需要注意各种细节,小心谨慎,以保证操作的正确性和稳定性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值