Dubbo 的调用过程:
首先客户端告知服务端要调用哪个接口、方法名、方法的参数类型、方法的参数值,还有可能存在多个版本的情况,所以还得带上版本号。
落地调用流程,首先远程调用需要定义协议,也就是相互约定我们要讲什么语言。
常见的三种协议形式:固定长度形式,特殊字符隔断形式,header+body形式。
dubbo支持的9种协议:
dubbo协议(默认)、rmi协议、hessian协议、http协议、webservice协议、thrift协议、memcached协议、redis协议、rest协议(就是restfull)
dubbo官网推荐我们使用dubbo协议。
dubbo 缺省协议(dubbo协议)采用单一长连接和 NIO 异步通信,适合小数据量大的并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的场景。不适合传送大数据量的服务,比如传文件、视频等,除非请求里很低。
dubbo 有多种协议,不同的协议默认使用不同的序列化框架。
例如dubbo协议使用 Hessian2 序列化,rmi协议 默认为java 原生序列化,http 默认为 json序列化,hessian 协议默认是hessian 序列化,webservice 协议默认是soap 文本序列化

简述一下就是客户端发起调用,实际调用的是代理类 Proxy,代理类最终调用的是 Client(默认netty),需要构造好协议头Header,然后将Java的对象序列化生成序列化体Body,然后网络调用传输,服务端的 NettyServer接到这个请求之后,Dispatcher分发给业务线程池ThreadPool,由业务线程调用具体的实现方法 Implement。
dubbo调用的三种方式:
oneway:不关心请求是否发送成功,啥都不用记,啥都不用管
异步调用:Dubbo天然就是异步调用的,可以看到client 发送请求后会得到一个 ResponseFuture,然后把future包装一下塞到上下文中,这样用户就可以从上下文中拿到这个future,然后等用户做了一波操作后再调用 future.get()方法等待结果。
同步调用:这是我们最常用的,也就是dubbo 框架帮我们异步转同步了。可以看到 dubbo 在源码中就调用了 future.get(),所以给用户的感觉是我调用了这个接口的方法后就被阻塞住了,必须要等结果到了之后才能返回,所以就是同步的。
异步和同步的区别就是 future.get() 在用户代码被调用还是在框架代码被调用。

Netty
Netty是一个广泛使用java网络编程的框架。Netty和Tomcat 的最大区别就在于通信协议,Tomcat是基于http协议的,它的实质就是一个基于http协议的web容器。但是Netty不一样,它能通过编程自定义各种协议,因为Netty能通过codec 自己来编码/解码字节流,完成类似redis访问的功能。
Netty 并发高,传输快,封装好。
Netty是一套基于NIO(非阻塞IO)开发的网络通信框架,对比BIO(阻塞IO),它的并发性能得到了很大的提升,原因是NIO的单线程处理连接的数量比BIO要高出很多。

原因就是 Selector。当一个连接建立以后,有两个步骤需要做,第一步是接收完客户端发过来的全部数据,第二部是服务端处理完请求后返回 response 给客户端。NIO 和 BIO 主要的区别在第一步。在BIO中,等待客户发送数据的过程是阻塞的,这样就只能造成一个线程只能处理一个请求的情况,而机器或者服务器能支持的最大线程数是有限制的,这就是BIO不能支持高并发的原因。而NIO中,当一个Socket 建立好后,Thread 并不会阻塞去接收这个Socket,而是将这个请求交给 Selector(类似经纪人),Selector 会不断的去遍历所有的Socket,一旦有一个Socket 建立完成,它就会通知Thread,然后Thread 处理完再返回给客户端,这个过程是不阻塞的,这样就能让一个Thread处理更多请求了。
五种IO: BIO、NIO、多路复用IO(它的两个步骤处理是分开的,也就是说,一个连接可能它的数据接收是线程A完成的,数据处理是线程B完成的,它比BIO能处理更多的请求)
信号驱动IO(主要用在嵌入式开发)、异步IO(它的数据请求和数据处理都是异步的,数据请求一次返回一次,适用于长连接的业务场景)
Netty传输快也是依赖了NIO的一个特性,零拷贝。我们知道,java的内存有堆内存、栈内存、字符串常量池等等。其中堆内存是占用内存空间最大的一块。也是java对象存放的地方,一般我们的数据要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达它的终点,如果数据量大,就会造成不必要的资源浪费。Netty 针对这种情况,使用了NIO中的另一大特性零拷贝,当它需要接受数据的时候,它会在堆内存之外开辟一块内存,数据就直接从IO读到了那块内存去,在Netty里面通过bytebuf 可以直接对这些数据进行操作,从而加快了传输速度。 ByteBuf 是 netty 的一个重要概念,它是netty 数据处理的容器,也是netty封装好的一个重要体验。
channel:表示一个连接,可以理解为每一个请求就是一个channel。

ChannelHandler:核心处理业务就在这里,用于处理业务请求。
ChannelHandlerContext:用于传输业务数据。
ChannelPipeline:用于保存处理过程需要用到的 ChannelHandler 和 ChannelHandlerContext
ByteBuf:是一个存储字节的容器,最大的特点就是使用方便,它既有自己的读索引和写索引,方便你对整段字节缓存进行读写,也支持get/set,方便你对其中每一个字节进行读写,它有三种使用模式:1、Heap Buffer 堆缓冲区,常用模式,数据存储在堆空间。
2、Direct Buffer 直接缓冲区,另一种常用模式,内存分配不发生在堆,jdk1.4 引入的NIO的 ByteBuffer 类允许jvm通过本地方法调用分配内存,这样做有两个好处,一是通过免去中间交换的内存拷贝,提升IO处理速度,直接缓冲区的内容可以驻留在垃圾回收扫描的堆区以外。二是 Direct Buffer ,在-XX:MaxDirectMemorySize 大小限制下,使用heap 之外的内存,GC对此“无能为力”,也就意味着规避了在高负载下频繁GC过程对应用线程中断的影响。
3、Composite Buffer 复合缓冲区,相当于多个不同的 ByteBuf 视图,z核实netty 提供的。
Codec:Netty 中编码和解码器,通过它能完成字节和 pojo,pojo和pojo 的相互转换,从而达到自定义协议的目的,这里面最有名的就是 HttpRequestDecode 和 HttpResponseEncode。

我们从0到1讲解下 netty的运作流程:
一开始创建两个 NioEventLoopGroup ,一个负责接收客户端请求(bossGroup),一个负责处理客户端的IO操作(workerGroup)。每个NioEventLoopGroup 中都有若干个 NioEventLoop。

NioEventLoopGroup 创建过程会创建 ThreadPerTaskExecutor ,MpscQueue,Selector 这三个主要的成员属性。ThreadPerTaskExecutor 负责创建底层的线程,MpscQueue 用于装载任务,实现异步串行无锁化,Selector 负责实现多路复用。
然后流程就走到了 NioServerSocketChannel 。创建操作会创建 id,Unsafe,pipeline 三个主要的成员属性,id作为唯一标识,Unsafe 负责底层读取 ACCEPT请求,pipeline 负责业务流程,其中pipeline 类似一个管道,里面包含很多 Handler,一个主要的 Handler 就是 ServerBootstrapAcceptor。初始化操作主要是向Channel设置选项和属性 以及向Pipeline中添加ChannelInitializer.通过选择器EventExecutorChooser#next得到NioEventLoop并与其关联.
注册操作会以任务的形式添加到NioEventLoop的MpscQueue中. 同时也就触发了NioEventLoop的启动流程.
绑定操作也是以任务的形式添加到NioEventLoop的MpscQueue中.
NioEventLoop的启动即通过ThreadPerTaskExecutor创建一个线程.
启动完成之后就进入无限循环执行过程.
会将之前放入MpscQueue中的注册任务和绑定任务进行处理. 注册就是向Selector注册JDK-Channel, 调用handlerAdded方法, 向Pipeline中添加ServerBootstrapAcceptor, 触发channelRegistered事件. 绑定端口, 触发channelActive事件, 向Selector设置ACCEPT事件.
至此服务端就可以接收客户端的连接请求了。

模型解释:
1、Netty 抽象出两组线程池 BossGroup 和 WorkerGroup ,BossGroup 专门负责接收客户端的连接,WorkerGroup 专门负责网络的读写。
2、BossGroup 和WorkerGroup 都是NioEventLoopGroup
3、NioEventLoopGroup 相当于一个事件循环线程组,这个组中含有多个事件循环线程,每一个事件循环线程是NioEventLoop。
4、每一个NioEventLoop 都有这个 Selector,用于监听注册其上的SocketChannel 网络通讯。
5、每个 boss NioEventLoop线程内部循环执行的步骤有3步,
5.1、处理accept 事件,与client 建立连接,生成 NioSocketChannel
5.2、将NioSocketChannel 注册到某个worker NioEventLoop 上面的 Selector
5.3、处理任务的队列,即runAllTasks
6、每个 worker NioEventLoop 线程循环执行的步骤
6.1、轮询注册到自己Selector 上的所有 NiioSocketChannel 的 read,write 事件
6.2、处理IO轮询,即read、write 事件,在对应的NioSocketChannel 处理业务。
6.3、runAllTasks 处理任务队列 TaskQueue 的任务,一些耗时的业务处理一般可以放入 TaskQueue中慢慢处理,这样不影响数据在 pipeline 中的流动处理
7、每个worker NioEventLoop 处理 NioSocketChannel 业务时,会使用pipeline(管道),管道中维护了很多 handler 处理器(ChannelHandler)来处理 Channel 中的数据。