zoukankan      html  css  js  c++  java
  • 简单说下Netty和RPC吧,大佬绕行

    Netty是什么?

    1. Netty是一个高性能、一部驱动的NIO框架,同时也是基于jAVA NIO实现的;
    2. Netty作为异步NIO框架,可以提供对TCP、UDP和文件传输的支持;
    3. 基于Netty的的异步机制-Future-Listener,用户可以主动获取/消息通知 方式,来获取IO操作的结果

    Netty的高性能?怎么个搞法?

    在IO编程过程中,当需要同事处理多个客户端的接入需求时,可以利用多线程/IO多路复用来处理;
    IO多路复用可以把多个请求阻塞复用到一个selector上,这样一个单线程selector就可以处理多个客户需求,降低开销,系统不需要每个需求都去创建线程,节省资源;

    进入netty正题

    1. 多路复用的通信方式
      1.1 服务端通信序列图

      1.2 客户端通信序列图

    Netty的IO线程NioEventLoop由于聚合了多路复用器selector,可以并发处理成百上千的客户端channel,且读操作书非阻塞的,效率更高;

    1. netty的异步通讯NIO

    由于Netty采用了异步通信模式,一个IO线程可以并发处理多个客户端线程(读/写),从根本上解决传统的阻塞IO模IO链接和线程1VS1的问题,架构性能、弹性伸缩能力和可靠性都有很大提升;

    • 提前记录下两个点:直接内存,堆内存?
      NIO中缓冲区是数据传输的基础,Netty基于JDK原生的ByteBuffer构造了ByteBuf,并进行了大量的优化。

      • 堆内存: 堆内存是由JVM管理的,相对于方法区/栈,对象的实例,数组等分配都存在堆上,GC要地;
      • 直接内存: JVM可以使用native方法在堆外分配内存,之后使用DircetByterBuffer对象作为这块内存的引用进行操作,这样不会对JVM的堆造成影响;只有在JVM-GC的时候才会去顺便清理下直接内存的废弃对象;
    • 关键点
      2.1 零拷贝

    • Netty的发送嗯哼接收ByteBuffer采用Direct Buffers,使用堆外直接内存进行Socket的读写,不需要进行字节缓冲区的二次拷贝。传统的方式会先存入堆内存Heap Buffers进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后进行Socket的读写,会多拷贝一次;

    • Netty提供了组合Buffer对象,可以聚合多个buffer对象,可以像操作一个buffer对象一样操作一组buffer对象;这样避免了传统操作中先将多个buffer对象合并的操作

    • Netty采用了TransferTo方法,它可以直接将文件缓冲区数据发送到目标的Channel,避免了传统的方式:通过循环write方法导致的内存拷贝问题;

    2.2 内存池.

    • netty的内存池是不依赖于JVM的,基于内存池的缓冲区重用机制,【没找到相关资料】
      2.3 高效的reactor线程模型
      • reactor单线程模型
        所有的IO操作都在同一个NIO线程(异步非阻塞)上完成:Acceptor 接收客户端的TCP 连接请求消息,链路建立成功之后,通过Dispatch 将对应的ByteBuffer
        派发到指定的Handler 上进行消息解码。用户Handler 可以通过NIO 线程将消息发送给客户端

      • reactor多线程模型
        所有的IO操作都是由一组NIO线程池来完成的:有专门一个NIO 线程-Acceptor 线程用于监听服务端,接收客户端的TCP 连接请求; 网络IO 操作-读、写等由一个NIO 线程池负责,线程池可以采用标准的JDK 线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO 线程负责消息的读取、解码、编码和发送

      • reactor主从多线程模型
        服务端用于接收客户端连接的不再是个1 个单独的NIO 线程,而是一个独立的NIO 线程池。
        Acceptor 接收到客户端TCP 连接请求处理完成后(可能包含接入认证等),将新创建的
        SocketChannel 注册到IO 线程池(sub reactor 线程池)的某个IO 线程上,由它负责
        SocketChannel 的读写和编解码工作。Acceptor 线程池仅仅只用于客户端的登陆、握手和安全
        认证,一旦链路建立成功,就将链路注册到后端subReactor 线程池的IO 线程上,由IO 线程负
        责后续的IO 操作

    2.4 无锁设计、线程绑定
    Netty采用无锁设计,串行操作。避免多线程的竞争;其实netty内部可以同时启动多个串行化线程

    Neety的NIOEventLoop读取到消息后,直接调用ChannelPiple的FireChannelRead(msg),只要用户不主动切换线程。一直由NioEventLoop来处理用户的Handler,期间不会线程切换;

    2.5 高性能的序列化框架
    netty默认支持google protobuf,用户可以实现其他的高性能序列化框架,例如Thrift的压缩二进制编码框架

    Netty的RPC实现

    RPC(远程调用):Remote Producedure Call

    3 关键技术

    • 服务的发布与订阅:zookeeper注册中心
    • 通信:使用Netty来做通信框架
    • Spring:使用Spring 来做beand加载配置
    • 动态处理:客户端使用代理模式,透明化服务调用
    • 消息编解码:使用pb序列化和反序列化

    3.1 核心流程

    3.2 通信流程

    netty一般使用channel.writeAndFlush()方法来发送二进制串,请求书异步请求;
    服务端接收到请求处理完后会发送给客户端;

    问题:当有多个线程请求到服务端,服务端怎么保证消息返回的准确性?

    1. requestID:会生成一个AtomicLong 全局唯一,并存储回掉对象到全局的ConCurrentHashMap
    2. sync获取回调对象的锁并自旋wait:
      当线程调用channel.writeAndFlush()发送消息后,紧接着会执行callback的get方法,视图获取远程的返回结果,get()方法内部使用sync获取callback对象锁:获取成功-查询是否有结果-没有结果-wait后释放锁继续等待;
    3. 当收到消息找到callback的锁唤醒线程-获取结果
    4. 接收到服务端返回结果,response中包含requsetID,发送给客户端;
      客户端的socket上有专门线程监听线程收到消息,获取到requestID,从前面的ConCurrentHashMap中获取到callBack对象,在用sync获取callbakc的锁。将结果设置到callback对象中。在调用callback.notifyAll()唤醒处于等待的线程;
      3.3 RMI实现方式

    JAVA RMI是java原生的远程调用编程接口,具体步骤如下

    1. 编写远程服务接口。实现Remote接口
    2. 实现类编写。继承UniccastRemoteObject
    3. 运行RMI编译器,创建客户端stub类和服务端skeleton类
    4. 启动RMI注册表,驻留这些服务
    5. RMI注册表中注册服务
    6. 客户端查找远程对象,并调用远程方法;
    //1:创建远程接口,继承java.rmi.Remote 接口
    public interface GreetService extends java.rmi.Remote {
        String sayHello(String name) throws RemoteException;
    }
    
    
    //2:实现远程接口,继承 java.rmi.server.UnicastRemoteObject 类
    public class GreetServiceImpl extends java.rmi.server.UnicastRemoteObject
            implements GreetService {
        private static final long serialVersionUID = 3434060152387200042L;
    
        public GreetServiceImpl() throws RemoteException {
            super();
        }
    
        @Override
        public String sayHello(String name) throws RemoteException {
            return "Hello " + name;
        }
    }
    
    //3:生成Stub 和Skeleton;
    //        4:执行rmiregistry 命令注册服务
    //        5:启动服务
            LocateRegistry.createRegistry(1098);
            Naming.bind("rmi://10.108.1.138:1098/GreetService",new GreetServiceImpl());
    //        6.客户端调用
            GreetService greetService=(GreetService)
            Naming.lookup("rmi://10.108.1.138:1098/GreetService");
            System.out.println(greetService.sayHello("Jobs"));
    
    

    4 其他RPC对比
    4.1 Protocol buffer

    4.2 THrift

    Apache Thrift,它采用接口描述语言定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码
    生成引擎可以在多种语言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa,
    Smalltalk 等创建高效的、无缝的服务,其传输数据采用二进制格式,相对 XML 和 JSON 体积更小,
    对于高并发、大数据量和多语言的环境更有优势。

    引用:

  • 相关阅读:
    【转载】 KL距离(相对熵)
    HTML5动画软件工具编辑器 HTML5动画分类 工具推荐
    Flash Actionscript AS3 渐变透明 mask遮罩
    HTML动画分类 HTML5动画 SVG库 SVG工具 Canvas动画工具
    Flash:彻底理解crossdomain.xml、跨swf调用。
    Flash: Event.PASTE Flash获取剪贴板内容 触发paste事件 how to get paste event
    Flash:TextField字体不显示/文字不显示/文字丢失
    Flash 矢量图和位图性能对比 导出为位图/缓存为位图 export as bitmap / cache as bitmap
    验证码去噪 分离背景 分离文字 最大类间方差
    Flash XSS 漏洞详解 根治的好办法
  • 原文地址:https://www.cnblogs.com/inyu/p/15412578.html
Copyright © 2011-2022 走看看