分布式架构知识总结
一、分布式理论
分布式系统概念和存在的问题
分布式系统,就是一个业务拆分成多个子业务,分布在不同的服务器节点,共同构成的系统称为分布式系统。
集群:多个人在一起作同样的事。
分布式:多个人在一起作不同的事。
特点:分布性、对等性、并发性、缺乏全局时钟、故障总数会发生
分布式系统存在的问题
通信异常:网络不确定性导致分布式系统无法顺利进行一次网络通信
网络分区:整个系统网络被切分,导致分布式系统出现局部小集群,小集群要完成整个分布式系统的功能。
节点故障:组成分布式系统的某个服务器出现宕机
三态:每次请求都存在的三种状态,失败、成功、超时。超时通常是发送过程中丢失或者响应过程中丢失。
分布式理论:一致性
数据在多份副本中存储时,各副本中的数据是一致的。
数据的ACID四原则:原子性、一致性、隔离性、持续性。
一致性分类
强一致性:要求系统写入什么,读出来的也会是什么,对系统性能影响最大,难实现。
弱一致性:约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据能够达到一致, 但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态。
弱一致性之读写一致性:用户读取自己写入结果的一致性,保证用户永远能够第一时间看到自己更新的内容,也就是写到了主库,但是读却走了从库,导致读写可能不一致,通过设定时间戳,让更新后一段时间都从主库读来实现。
弱一致性之单调读一致性:本次读到的数据不能比上次读到的旧,也就是第一次读主库最新值,第二次读从库还是旧值,通过根据用户ID计算一个hash值,再通过hash值映射到机器,让用户每次都访问一台机子来实现。
弱一致性之因果一致性:节点 A 在更新完某个数据后通知了节点 B,那么节点 B 之后对该数据的访问和修改都是基于 A 更新后的值。
弱一致性之最终一致性:最弱一致性模型,不考虑所有的中间状态的影响,只保证当没有新的更新之后,经过一段时间之后,最终系统内所有副本的数据是正确的。它最大程度上保证了系统的并发能力,在高并发的场景下,它也是使用最广的一致性模型。
分布式理论:CAP定理
一个分布式系统不可能同时满足一致性(C:Consistency),可用性(A: Availability)和分区容错性(P:Partition tolerance)这三个基本需求,最多只能同时满足其中的2个。
一致性
目标:
1.商品服务写入主数据库成功, 则想从数据库查询数据也成功
2.商品服务写入主数据库失败,则向从数据库查询也失败
实现:
1.写入主数据库后要数据同步到从数据库,同步有一定延迟。
2.写入主数据库后,在向从数据库同步期间要将从数据库锁定, 等待同步完成后在释放锁,以免在写新数据后,向从数据库查询到旧的数据。
可用性
目标:
1.从数据库接收到数据库查询的请求则立即能够响应数据查询结果
2.从数据库不允许出现响应超时或错误
实现:
1.写入主数据库后要将数据同步到从数据
2.由于要保证数据库的可用性,不可以将数据库中资源锁定
3.即使数据还没有同步过来,从数据库也要返回查询数据, 哪怕是旧数据,但不能返回错误和超时.
分区容错性
目标:
1.主数据库想从数据库同步数据失败不影响写操作
2.其中一个节点挂掉不会影响另一个节点对外提供服务
实现:
1.尽量使用异步取代同步操作,如使用异步方式将数据从主数据库同步到从数据库, 这样节点之间能有效的实现松 耦合;
2.添加数据库节点,其中一个从节点挂掉,由其他从节点提供服务。
分布式理论:BASE理论
BASE:全称:Basically Available(基本可用),Soft state(软状态),和 Eventually consistent(最终一致性)
对CAP中一致性和可用性权衡的结果,即无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。
Basically Available(基本可用):
分布式系统在出现不可预知故障的时候,允许损失部分可用性,比如12306抢票,他给你延时查询,响应非常久,又比如双十一抢购,订单付款时内部出现某种错误,网页这边提示你数据加载失败,让你重试,即不失败也不成果,进入降级处理。
Soft state(软状态):允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本之间进行数据同步的过程中存在延迟。
Eventually consistent(最终一致性):最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
分布式理论:一致性协议2PC
两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Preparephase)、提交阶段(commit phase),2是指两个阶段,P是指准备阶段,C是指提交阶段。
准备阶段(Prepare phase):
事务管理器给每个参与者发送Prepare消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。(Undo日志是记录修改前的数据,用于数据库回滚, Redo日志是记录修改后的数据,用于提交事务后写入数据文件)
提交阶段(commit phase):
如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的锁资源。
缺点
同步阻塞:他需要等第一阶段所有节点全部完成之后才能执行第二阶段。
单点问题:严重依赖于事务管理协调者,一旦协调者出现问题导致整个流程无法完成。
数据不一致:第二阶段事务管理器逐个向资源节点发生提交请求,当发送到一半,事务管理器宕机了,前面的资源节点会提交,后面的回滚,导致数据不一致。而且当资源节点出现问题无法向协调者发送响应信息,事务管理者只能依赖超时机制进行事务中断。
分布式理论:一致性协议3PC
将 2PC 的提交阶段过程一分为三,形成了由 CanCommit、PreCommit和doCommit三个阶段组成的事务处理协议。
CanCommit:协调者给参与者发送事务响应等待全部回应。
PreCommit:协调者收到全部参与者响应,yes发送执行事务预提交,并反馈ACK,如果有一个参与者未反馈或者反馈no,中断事务。
doCommit:协调者收到所有参数值ACK反馈,向所有参与者发送提交指令,参与者完成之后向协调者发送ACK响应,但是有一个问题,也就是进入第三阶段,如果协调者因宕机等原因没有向参与者发送提交doCommit请求或回滚abort请求,参与者到达超时时间自动提交,也就是如果是要准备回滚的话,就出现了问题。
2PC对比3PC
1) 协调者和参与者都设置了超时机制,降低了整个事务的阻塞时间和范围,解决了之前的同步阻塞。
2) 通过CanCommit、PreCommit、DoCommit三个阶段的设计,相较 于2PC而言,多设置了一个缓冲阶段保证了在最后提交阶段之前各参与节点的状态是一致的。
3) 3PC协议并没有完全解决数据不一致问题。
分布式理论:一致性算法Paxos
此算法用于解决分布式系统一致性问题。
当出现多个参与者,要保证数据事务一致性,就引入了3PC进行协调,协调者也可能宕机,所以协调者也做了集群,当每个参与者发送了不同的指令给协调者,协调者就必须有一个真正的决策者来保证数据的一致性。
提案(Proposal)
提案 (Proposal):Proposal信息包括提案编号 (Proposal ID) 和提议的值 (Value),最终要达成一致的value就在提案里。
Paxos算法的角色
Client:客户端向分布式系统发出请求 ,并等待响应。
Proposer:提案发起者提倡客户请求,试图说服Acceptor对此达成一致,并在发生冲突时充当协调者以推动协议向前发展。
Acceptor:决策者可以接受(accept)提案;如果某个提案被选定(chosen),那么该提案里的value就被选定了。
Learners:最终决策学习者充当该协议的复制因素。
一致性算法的保证
1)在这些被提出的提案中,只有一个会被选定 。
2)如果没有提案被提出,就不应该有被选定的提案。
3)当一个提案被选定后,那么所有进程都应该能学习(learn)到这个被选定的value。
Paxos算法提案规则
总结:提案规则P2c =>P2b=>P2a=>P2,通过P2和P1来保证一致性,概括就是决策者必须接受收到的第一个提案,当只有一个决策者,那么第一个提案就是最终提案,当多个决策者,以半数以上决策者接收的提案为最终提案,当决策者选定了一个提案,后续提案只能增长序列编号,不能更改提案的值。
Proposer生成提案规则->Acceptor接受提案规则
Proposer生成提案之前,应该先去『学习』已经被选定或者可能被选定的value,然后以 该value作为自己提出的提案的value。如果没有value被选定,Proposer才可以自己决定value的值。
理解:提案者1先发送一个prepare给决策者,并携带一个map,这个map只有key,编号n,value是null,当决策者接受了这个提案,就把当做了最终提案保存了下来,并返回了一个空value回来,这里有一个规则,如果决策者是多个,需要超过半数返回才行,如果没有超过半数,重新提交prepare给决策者,接着提案者1再往map里面放入值,提交给决策者,决策者就把这个map替换了之前的空null的map,并返回ack,如果超过半数返回了ack,此value就被确定,否则重新提交prepare给决策者,当完成后如果又有另一个提案者2提交了提案,他的编号是n+1,决策者接收了,但是决策者已经有最终提案了,这时候他会把value返回给提案者2,让提案者2学习,把自己map集合的value值替换成决策者返回的值,提案者学习完毕,再次提交,这时候决策者就接受了提案者2的提案,把之前的提案进行了替换,而且编号低于n+1的提案会被决策者直接拒绝,这样值没变,保证了数据的一致性,还可以通过编号实现对不符合规则的提案过滤。
Learner学习被选定的value
当决策者接受了一个提案,就会把这个提案发给所有学习者,来保证数据一致性。
保证Paxos算法的活性
两个Proposer依次提出了一系列编号递增的提案,但是每次根据响应塞值提交又因为编号不一致,被另一个提案者编号给递增,导致又恢复到最开始提交prepare,最终陷入死循环,没有value被选定。
解决:通过选取主Proposer,并规定只有主Proposer才能提出议案。这样一来只要主Proposer和过半的Acceptor能够正常进行网络通信,那么只能是主Proposer提出一个编号更高的提案,该提案终将会被批准,这样通过选择一个主 Proposer,整套Paxos算法就能够保持活性。
分布式理论:一致性算法Raft
Raft算法分为两个阶段,首先是选举过程,然后在选举出来的领导人带领进行正常操作,主要用于管理复制日志的一致性算法。
Raft算法三模块:领导人选举、日志复制、安全性。
领导人Leader选举
Raft通过选举一个领导人,然后给予他全部的管理复制日志的责任来实现一致性。
三个角色(任何服务器都可以当三个角色之一):
领导者(leader):处理客户端交互,日志复制等动作,一般一次只有一个领导者
候选者(candidate):候选者就是在选举过程中提名自己的实体,一旦选举成功,则成为领导者
跟随者(follower):类似选民,完全被动的角色,这样的服务器等待被通知投票
理解:当服务启动的时候,所有服务器follower都是初始状态,每个服务器都有一个定时器,超时时间为election timeout(一般为150-300ms),当某个服务器达到超时时间,他就成为了候选者,先给自己投上一票,然后发送消息给其他服务器,当其他服务器超过半数收到了他的消息,相当于获取到了选票,他就成了领导者,而其他服务器全部成了跟随者,这时候领导者就开始根据间隔时间向跟随者发送心跳检测包,证明我还活在,也就是心跳机制,而跟随者每次接受到消息,就初始化自己内部的定时器,当某个服务器定时器达到超时时间,没有收到领导者的消息,那么跟随者会觉得领导者挂了,他就摇身一变称为候选者,开始篡位,重复之前的过程,成为领导者,当他成为领导者之后,当前任领导者就算回来了,也只能变成跟随者。
特殊情况:四个服务器,当其中两个服务器同时达到超时成为候选者,并且每个服务器拿到自己一票,另外一个服务器一票,这时候的机制就是这两个服务器重新定时,先达到超时的服务器成为候选者,并发送通知进一步成为选举者。
日志复制(保证数据一致性)
Leader选出后,就开始接收客户端的请求。Leader把请求作为日志条目(Log entries)加入到它的日志中,然后并行的向其他服务器发起 AppendEntries RPC复制日志条目。当这条日志被复制到大多数服务器上,Leader将这条 日志应用到它的状态机并向客户端返回执行结果。
1)客户端的每一个请求都包含被复制状态机执行的指令。
2)leader把这个指令作为一条新的日志条目添加到日志中,然后并行发起 RPC 给其他的服务器,让他们复制这条 信息。
3)跟随者响应ACK,如果 follower 宕机或者运行缓慢或者丢包,leader会不断的重试,直到所有的 follower 最终都 复制了所有的日志条目。
4)通知所有的Follower提交日志,同时领导人提交这条日志到自己的状态机中,并返回给客户端。
二、分布式系统设计策略
分布式常用设计策略:如何检测当前节点还活着? 如何保障高可用? 容错处理? 负载均衡?
心跳检测机制
心跳顾名思义,就是以固定的频率向其他节点汇报当前节点状态的方式。收到心跳,一般可以认为一个节点和现在的网络拓扑是良好的。心跳汇报时,一般也会携带一些附加的状态、元数据信息,以便管理。
理解:Client请求Server,Server转发请求到具体的Node获取请求结果。Server需要与三个Node节点保持心跳连接,确保Node可以正常工作,若Server没有收到Node3的心跳时,Server认为Node3失联,当Node3不一定是宕机了,可能是网络中断、任务繁忙导致检测超时等等,通过周期检测心跳机制、累计失效检测机制来判断节点是否挂掉,如果真正挂掉,从集群中移除,等恢复后再加进来。
高可用设计
系统架构设计中必须考虑的因素之一,通常是指经过设计来减少系统不能提供服务的时间。
系统高可用性的常用设计模式包括三种:主备(Master-SLave)、互备(Active-Active)和集群(Cluster)模式。
主备模式
最常用的模式,当主机宕机时,备机接管主机的一切工作,待主机恢复正常后,按使用者的设定以自动(热备)或手动(冷备)方式将服务切换到主机上运行,数据库称为MS模式,MySQL、Redis就采用MS模式实现主从复制,保证高可用。
master中所有操作都会以“事件”的方式记录在二进制日志中,也就是bin-log,其他数据库作为slave通过一个I/O线程与主服务器保持通信,并监控master的二进制日志文件的变化,如果发现master二进制日志文件发生变化,则会把变化复制到自己的relay-log日志中,然后slave的一个SQL线程会把相关的“事件”执行到自己的数据库中,以此实现从数据库和主数据库的一致性,也就实现了主从复制。
互备模式
两台主机同时运行各自的服务工作且相互监测情况,开启互相备份同步,只有对写要求较高,需要多台数据库服务器存储写入数据,比如微博之类的网站。
集群模式
集群模式是指有多个节点在运行,同时可以通过主控节点分担服务请求。如Zookeeper。集群模式需要解决主控节 点本身的高可用问题,一般采用主备模式来把某个节点当做master。
容错性设计
提高系统对于错误包容的能力,保障分布式环境下相应系统的高可用或者健壮性,也提升了系统的安全性。
比如Redis的缓存穿透。
当一个请求进来时,从缓存中查询不到,这时候就会进去数据库查,如果是一个恶意的请求,比如id为-1,这个值根本不存在,制造大量请求进行攻击,越过缓存,数据库服务器可能就会承受不住压力而宕机,这时候就需要一个容错性设计来保证数据库的安全,可以通过布隆过滤器或者在第一次请求,为null时仍然返回一个值存储缓存,并设置一定的过期时间,再次请求就会直接经过缓存返回,来保证系统的高可用。
负载均衡策略
使用多台集群服务器共同分担计算任务,把网络请求及计算分配到集群可用的不同服务器节 点上,从而达到高可用性及较好的用户操作体验。
负载均衡器有硬件解决方案F5,也有软件解决方案Nginx、LVS等等。
负载均衡策略:
1) 轮询:默认就是轮询,根据Nginx配置文件中的顺序,依次把客户端的Web请求分发到不同的后端服务器。
2) 最少连接:当前谁连接最少,分发给谁。
3) IP地址哈希:确定相同IP请求可以转发给同一个后端节点处理,以方便session保持。
4) 基于权重的负载均衡:配置Nginx把请求更多地分发到高配置的后端服务器上,把相对较少的请求分发到低配服务器。
三、分布式架构网络通信
基本原理
网络通信就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络IO来实现,其中传输协议比较出名的有tcp、udp等等,tcp、udp都是在基于Socket概念上为某类应用场景而扩展出的传输协议,网络IO,主要有bio、nio、aio三种方式。其实就是使用不同的传输协议和IO技术实现服务器之间的远程方法调用。
TCP:面向连接的协议,速度不快,安全,因为他要经过三次握手,安全性要求更高四次握手。
UDP:广播协议,面向无连接,速度快,不安全,就是相当于广播一样只管发出去就完事了。
BIO:阻塞IO NIO:非阻塞IO AIO:非阻塞IO
RPC基本概念
RPC远程过程调用,核心模块就是通讯和序列化,他不是具体的技术,而是指整个网络远程调用过程,常用RPC实现框架Dubbo、Hessian、HSF等等。
RPC四个核心的组件,分别是Client,Client Stub,Server以及Server Stub,即客户端、客户端存根、服务端、服务端存根。
1) 客户端以接口方式调用客户端存根。
2) 客户端存根收到调用后,把方法、参数等封装成消息体进行序列化成二进制文件,从而在网络中传输。
3) 客户端存根将请求发送给服务器存根。
4) 服务器存根收到消息后对消息体进行反向序列化。
5) 服务端存根根据解码结果调用本地服务端。
6) 服务端进行服务处理,即执行方法并返回结果。
7) 服务端存根收到结果,将结果封装成消息体,并进行序列化成二进制文件。
8) 服务端存根返回消息给客户端存根。
9) 客户端存根收到消息后对消息体进行反序列化解码。
10)客户端存根返回解码结果,客户端得到最终结果。
RPC的目标是要把2、3、4、7、8、9这些步骤都封装起来。
RMI远程方法调用
远程方法调用 (Remote Method Invocation),是java原生支持的远程调用 ,采用JRMP(Java Remote Messageing protocol)作为通信协议,纯java版本的分布式远程调用解决方案。
客户端
1)存根/桩(Stub):远程对象在客户端上的代理。
2)远程引用层(Remote Reference Layer):解析并执行远程引用协议。
3)传输层(Transport):发送调用、传递远程方法参数、接收远程方法执行结果。
服务端
1) 骨架(Skeleton):读取客户端传递的方法参数,调用服务器方的实际对象方法,并接收方法执行后的返回值。
2) 远程引用层(Remote Reference Layer):处理远程引用后向骨架发送远程方法调用。
3) 传输层(Transport):监听客户端的入站连接,接收并转发调用到远程引用层。
注册表
URL形式注册远程对象,并向客户端回复对远程对象的引用。
运行过程:首先启动server服务端,向注册表发布对象,再启动客户端,客户端就从注册表获取远程对象引用,接着客户端生成对应的代理对象,也就是Stub桩对象,他和远程对象具有相同的接口和方法列表,接着通过远程引用层Remote Reference Layer进行转化成远程引用对象,传递给传输层Transport,由传输层发送TCP协议到服务端的传输层Transport,接着服务端的传输层调用远程引用层Remote Reference Layer,把远程引用对象转化成服务端本地对象,然后传递给骨架Skeleton,骨架根据请求去调用服务端进行对应方法执行,并获取返回结果,接着骨架Skeleton又一层一层往回返,最终客户端获取结果。
同步、异步、阻塞、非阻塞
同步:用户进程触发IO操作等待或者轮训的方式查看IO操作是否就绪。
异步:当一个异步进程调用发出之后,调用者不会立刻得到结果。而是在调用发出之后,被调用者通过状态、通知来通知调用者,或者通过回调函数来处理这个调用。
阻塞:当一个线程需要获取一把锁,一直等待这个锁释放。
非阻塞:当一个线程需要获取一把锁,并不会一直等待,他跑去做其他事,等通知者告知他锁被释放,他在回来获取锁接着干事。
BIO
同步阻塞IO,B代表blocking,jdk1.4之前唯一IO模型。
一个连接一个线程,即客户端有连接请求时服务器端就启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池改善,但是同样的无法避免大量线程的创建,并发请求效率低。
NIO
同步非阻塞IO,JDK 1.4 及以上版本。
一个请求一个通道,即客户端发送的连接请求都会注册到多路复用器上,多路复用器(也称为选择器)轮询到连接有IO请求时才启动一个线程进行处理。
通道(Channels)
NIO 新引入的最重要的抽象是通道的概念。Channel数据连接的通道。 数据可以从Channel读到Buffer,也可以从buffer写到Channel中。
缓冲区(Buffer)
就是存数据的地方,通过缓冲区提高读写效率,通道channel可以向缓冲区Buffer中写数据,也可以向buffer中存数据。
选择器(Selector)
使用选择器,借助单一线程,就可对数量庞大的活动 I/O 通道实时监控和维护。
BIO模型中,一个连接来了,会创建一个线程,对应一个while死循环,死循环的目的就是不断监测这条连接上是否有数据可以读,而NIO他创建了一个多路复用器selector,连接来了之后现进多路复用器,通过检查这个selector,不需要再批量死循环,就可以批量监测出有数据可读的连接,进而读取数据。
AIO
异步非阻塞IO,JDK7开始支持。
当有流可以读时,操作系统会将可以读的流传入read方法的缓冲区,并通知应用程序,对于写操作,OS将write方法的流写入完毕,操作系统会主动通知应用程序。因此read和write都是异步的,完成后调用回调函数通知。
Netty
Netty 是由 JBOSS 提供一个异步的、 基于事件驱动的网络编程框架。
NIO缺点
1)NIO 的类库和 API 繁杂,使用麻烦。
2)可靠性不强,开发工作量和难度都非常大。
3)NIO 的 Bug。例如 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。
Netty优点
1)对各种传输协议提供统一的API 。
2)高度可定制的线程模型——单线程模型、一个或多个线程池模型。
3)更好的吞吐量,更低的等待延迟 。
4)更少的资源消耗 。
5)最小化不必要的内存拷贝。
netty模型
Netty 抽象出两组线程池, BossGroup 专门负责接收客 户端连接, WorkerGroup 专门负责网络读写操作。 NioEventLoop 表示一个不断循环执行处理 任务的线程, 每个 NioEventLoop 都有一个 selector, 用于监听绑定在其 上的 socket 网络通道。 NioEventLoop 内部采用串行化设计, 从消息的读取->解码->处理->编码->发送, 始终由 IO 线 程 NioEventLoop 负责。
Netty核心组件
ChannelHandler 接口及其实现类,相当于之前NIO里面的accept、read、write方法。
ChannelPipeline 是一个Handler的集合,它负责处理和拦截 inbound 或者 outbound 的事件和操作,相当于一个 贯穿 Netty 的链。
ChannelHandlerContext这是事件处理器上下文对象,Pipeline链中的实际处理节点。每个处理节点ChannelHandlerContext中包含一个 具 体 的 事 件 处 理 器 ChannelHandler , 同 时 ChannelHandlerContext 中也绑定了对应的 pipeline 和 Channel 的信息,方便对 ChannelHandler 进行调用。
ChannelFuture 核心,表示Channel 中异步 I/O 操作的结果,在 Netty 中所有的 I/O 操作都是异步的, I/O 的调用会直接返回,调用者并不能立刻获得结果,但是可以通过 ChannelFuture 来获取 I/O 操作的处理状态,结果要等执行完毕后获取。
EventLoopGroup 和其实现类 NioEventLoopGroup,EventLoopGroup(最上面模型中的Boss Group) 是一组 EventLoop(可以理解成一个线程,最上面模型中的NIOEventLoop) 的抽象, Netty 为了更好的利用多核 CPU 资源, 一般会有多个 EventLoop 同时工作,每个EventLoop 维护着一个Selector实例,线程池一般是两个,一次处理连接,一次进行操作。
ServerBootstrap 和 Bootstrap ServerBootstrap 是 Netty 中的服务器端启动助手,通过它可以完成服务器端的各种配置;Bootstrap是Netty 中的客户端启动助手,通过它可以完成客户端的各种配置。
基于Netty自定义RPC
两种常用远程调用
1)是基于HTTP的restful形式的广义远程调用,以spring could的feign和restTemplate为代表,采用的协议是HTTP的7层 调用协议,并且协议的参数和响应序列化基本以JSON格式和XML格式为主。
2)是基于TCP的狭义的RPC远程调用,以阿里的Dubbo为代表,主要通过netty来实现4层网络协议,NIO来异步传输, 序列化也可以是JSON或者hessian2以及java自带的序列化等,可以配置。
运转实现图
服务端
1) 通过main方法启动服务器,设定ip和端口,调用实现类的startServer方法。
2) 在startServer方法创建启动引导类并监听要做的事,通过自定义UserServiceHandler来实现自定义逻辑的监听来并加到启动引导类的通道中。
3) 当有读操作出现,就会进入自定义处理器,执行相应逻辑并返回结果,最后把结果写回客户端。
客户端
1) 通过main方法启动服务器,首先调用createProxy,传递接口类和参数,进行代理对象的构造。
2) 当代理对象创建之后,调用代理对象对应的方法时,就会进去JDK动态代理的invoke拦截方法内部进行客户端初始化。
3) 初始完毕之后,开始设置参数,即最开始定义参数拼接方法传递参数
4) 当参数也设置完毕之后就可以通过线程池对call方法进行调度,进行写操作,写完阻塞,等待服务器返回数据之后唤醒继续写,这是通过互斥锁来实现读写交替。
5) 最后返回结果给客户端。