《深入理解Dubbo原理与实践》读书笔记

目录

第1章 Dubbo-高性能RPC通信框架

第2章 开发第一款Dubbo应用程序

第4章 Dubbo扩展点加载机制

第5章 Dubbo启停原理解析

第6章 Dubbo远程调用

第7章 Dubbo集群容错

第8章 Dubbo扩展点

第9章 Dubbo高级特性

第10章 Dubbo过滤器

第11章 Dubbo注册中心扩展实践

第13章 Dubbo未来展望


第1章 Dubbo-高性能RPC通信框架

1.1 应用架构演进过程:EJB单体 --> 垂直化服务( MVC )--> SOA(WEB Service,ESB) --> 微服务 --> 云原生(流式计算)

EJB:分展示层,业务逻辑层,数据存储层。

优点:第一,分层设计确定了不同团队的分工,职责清晰,分工明确,产生了前后端和数据库团队。第二,开发简单,所有类都能直接本地引用及使用,事务处理只需要依赖数据库事务。由于大多是是面向内部用户,并发不高,ejb的稳定,基本能满足需求。

缺点:第一,单体,随着应用的增大性能下降。第二,业务之间的耦合严重,随着业务复杂度的增加,开发人员的流动,维护变得越来越难。第三,大量使用xml作为配置文件,后期要配置一个可用的服务学习成本高。

垂直化服务MVC:主要解决问题是避免单体的臃肿,前后端团队划分更清晰,工作效率更高

优点:分层更加简单,架构更轻量级,开发效率提升,单元测试完善。缺点:还是在一个 WAR,虽然后期做了垂直拆分,服务之间完全独立,不能远程调用,很多基础代码不能复用,还是无法解决单体应用的臃肿问题。

分布式应用(SOA+微服务):主要解决的问题是服务的复用

SOA:常见的有esb和web service将单一进程的应用做了拆分,服务之间通过约定的协议通信。有几个特点:明确的协议,明确的接口,合作方式的改变(后端分工更细),通信方式由xml改为json。缺点:web service通信协议笨重,服务化管理不完善。esb太重,系统变更影响总线变更。esb适合老企业内部,不同开发语言,不同来源的系统,如外购,自研,应用之间没有统一交互协议。

微服务化,与soa相比:

第一,目的不同,esb强调业务流程编排,为了集成历史应用。而微服务可以有效拆分服务,实现敏捷部署与交付。

第二,服务粒度不同。微服务划分更细。

第三,协议不同。微服务通常有统一协议,比较难兼容老系统。

云原生:容量动态规划,提高资源利用率。让基础设施下沉,减少服务中大量业务无关的资源库,进一步简化系统。

1.2 Dubbo的发展历史

2011年开源,2014.10-2017.8 暂停维护,2017.9重启维护,2018.2捐献给Apache。

1.3 Dubbo是什么,解决什么问题

Dubbo是阿里SOA服务化治理方案的核心框架,

解决的问题:高性能、透明RPC调用,服务的自动注册与发现,自动负载与容错,动态流量调度,依赖分析与调用统计。

第2章 开发第一款Dubbo应用程序

三种方式实现:基于XML方式,注解,API (最灵活,网关采用这种)

第3章 Dubbo注册中心

支持多协议多注册中心暴露

1. ZooKeeper注册中心:

生产中经过验证

4种目录,/dubbo/service下面是providers,consumers,routers,configurations

订阅/发布:事件通知+客户端拉取,ZooKeeper是通过watcher监听事件,消费者会订阅providers,routers,configurations这三个目录,客户端与注册中心保持长连接。第一次全量拉取数据,当有变化时再次全量拉取,所以当服务节点较多是会对网络造成很大的压力。

2.Redis注册中心:

订阅/发布:使用过期机制+publish/subscribe通道。订阅方首次连接注册中心,全量拉取并缓存到本地。提供者发布服务会在redis创建一个key,发送一条register消息,并设置过期时间,然后周期刷新过期时间。注册中心去发现过期的key,会遍历本地缓存的key列表,如果发现key过期了,则说明服务提供者下线了,则发布unregister消息。

redis的publish/subscribe通道不是消息可靠的,redis注册中心没有经过大规模生产验证。

模板设计模式:注册中心的逻辑部分

AbstractRegistry实现了 Registry接口中的注册、订阅、查询、通知等方法,还实现了磁

盘文件持久化注册信息这一通用方法。但是注册、订阅、查询、通知等方法只是简单地把URL

加入对应的集合,没有具体的注册或订阅逻辑。

FailbackRegistry又继承了 AbstractRegistry,重写了父类的注册、订阅、查询和通知等

方法,并且添加了重试机制。此外,还添加了四个未实现的抽象模板方法,

工厂模式:注册中心的实现

第4章 Dubbo扩展点加载机制

Dubbo良好的扩展性依赖各种设计模式和加载机制

JDK自带SPI:在META-INF/services/ 目录下创建接口全路径的文件,文件内容为实现类,多个实现类用分行符分割

Dubbo的SPI:默认扫描三个目录:META-INF/services/ ,META-INF/dubbo,META-INF/dubbo/internal,内容是key-value形式

Dubbo相对JAVA自带SPI的改进:

1. JDK标准SPI会一次性实例化扩展点的所有实现,初始化耗时,如果没有用上浪费。Dubbo SPI只会加载配置文件的类,并且会分类保存到内存里,不会立刻初始化,有更好的性能

2. JDK标准SPI会扩展加载失败会把失败原因吃掉,拿不到真实的失败原因。Dubbo SPI加载失败会先抛出真实异常。

3. Dubbo SPI增加了对扩展IoC和AOP的支持。

Dubbo扩展点缓存:

1. Class缓存:获取扩展类时,先从缓存中读取,缓存没有则加载配置文件,根据配置把Class缓存到内存,并不会全部初始化。

2. 实例缓存:缓存实例,先从缓存读取,没有再去加载,按需加载,性能更好。

扩展点分类:

@SPI注解使用在类,接口,枚举类,一般是在接口上

1. 自动包装:根据构造函数参数,包装扩展点。装饰器模式。

2. 自动加载:扩展点是另一个扩展点的成员属性,并且有setter方法,则自动加载进来。

3. 自适应:通过传入参数激活一个具体的实现类,@Adaptive({param1, param2}),可以传入多个参数,依次匹配,没有就抛出异常。注解在方法上则根据入参动态生成实现类,如果注解在类上,则固定实现类,类似于策略模式直接确定实现类。可以在类,接口,枚举类和方法上。

4. 自动激活:标注@Activate的扩展点会自动激活,可以激活多个具体的实现类,比如Filter,需要多个过滤器。可以在类,接口,枚举类和方法上。

自适应查找实现类顺序:优先@Adaptive里的参数作为key,然后是@SPI的参数,最后是类名作为key

扩展点首先会生成字符串类,最后是动态代码编译器生成Class,编译器工具库有CGLIB,ASM,Javassist

第5章 Dubbo启停原理解析

配置优先级:-D传递给JVM参数 > 代码或者XML配置 > 配置文件

provider端的配置会透传到客户端,如果客户端也有,则使用客户端的配置。

服务的属性值会绑定到URL

服务暴露:

1. 支持多注册中心多协议(比如Dubbo和Rest)暴露,先转换成Invoker,再转为Exporter。Invoker可以理解为一个真实的服务对象实例。Exporter通常会使用NettyServer对外打开端口监听,通过过滤器包裹Invoker,请求过来会做服务的拦截最后是调用Invoker。

2. 可以远程暴露,也支持本地JVM暴露(injvm协议)

消费暴露:可用lazy属性控制是否立即与远程建立TCP连接,默认是Netty连接。支持多注册中心消费。支持直连服务消费,绕开注册中心。

优雅停机原理:

1. 收到kill 9进程退出信号,Spring容器会触发容器销毁事件。

2. provider端会取消注册服务元数据信息。

3. consumer端会收到最新地址列表(不包含准备停机的地址)。

4. Dubbo协议会发送readonly事件报文通知consumer服务不可用。

5. 服务端等待已经执行的任务结束并拒绝新任务执行。

既然注册中心已经通知了最新服务列表,为什么还要再发送readonly报文呢?这里主要考虑到注册中心推送服务有网络延迟,以及客户端计算服务列表可能占用一

些时间。Dubbo协议发送readonly时间报文时,consumer端会设置响应的provider为不可用状态,下次负载均衡就不会调用下线的机器。

第6章 Dubbo远程调用

Dubbo调用流程,路由和负载均衡都在客户端实现

Directory:获取服务提供列表

Cluster:容错接口,提供Failover,Failfast策略

Router:路由

LoadBalance:负载均衡

服务端两个线程池:I/O线程池(负责接收请求,不宜处理耗时请求),业务线程池(处理耗时请求)

解决粘包:使用特殊字符:0xdabb魔法数

全局请求ID标识来做异步并发,发起请求后调用DefaultFuture的get方法阻塞线程,然后id和DefaultFuture放到HashMap,结果回来后,通过id拿到DefaultFuture,唤醒阻塞的线程。

编解码器原理比较复杂,没有具体研究

心跳Handler:默认客户端和服务器端都会发送心跳,用来保持TCP长连接状态。一分钟没有读写会发送心跳报文。三分钟没有读写,如果是客户端则重连,如果服务器端则断开连接

第7章 Dubbo集群容错

Cluster容错层包括几个接口:Cluster,Directory,Router,LoadBalance

容错机制:

1.Failover:失败重试,默认重试2次,Dubbo的默认容错机制。会做负载均衡。通常在读操作或者幂等写操作

2.Failfast:快速失败,请求失败后快速返回异常结果,不做重试。会做负载均衡。在非幂等操作上。

3.Failsafe:出现异常时直接忽略异常返回空结果集,会做负载均衡。不关心调用是否成功的场景,比如写日志。

4.Failback:失败后放到定时线程池并返回一个空的结果集,定时重试,重试失败后会继续重试。用在一些异步或者要求最终一致性的场景,会做负载均衡。

5.Forking:同时调用多个相同的服务,其中一个返回则立即返回结果。只有所有失败才会往阻塞队列放一个失败结果。用在实时性要求高的场景,但是会浪费资源。forks设置最大并行调用数

6.Broadcast:广播调用所有可用服务,任意一个节点报错则报错。不需要负载均衡。用于服务状态更新后的广播。

7.Mock:失败时,返回伪造的响应结果。或者强制返回伪造结果,不会发起远程调用。只有拦截到RpcException才会启用。

8.Avaalable:不会负载均衡,遍历所有服务列表,找到第一个可用的节点直接请求并返回结果。没有可用节点则抛出异常。

9.Mergeable:合并多个请求结果。

Mock容错(熔断):

配置方式1:可以在配置文件中配置

<dubbo:reference interface="com.foo.BarService" mock="true" />

配置方式2 :

<dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock" /> <i

配置方式3:

<dubbo:reference interface="com.foo.BarService" mock="return null" /> <i

如果Mock配置了 true或default,则实现的类名必须是接口名+Mock,如配置方式1

否则会直接取Mock参数值作为Mock实现类,如配置方式2

package com.foo;

public class BarServiceMock implements BarService {

public String sayHello(String name) {

return ”容错数据“;// 可以伪造容错数据,此方法只在出现RpcException时被执行

}

}

当接口配置了 Mock,在RPC调用抛出RpcException时就会执行Mock方法。最后一种return null的配置方式通常会在想直接忽略异常的时候使用。

在dubbo-admin中通过override协议更新Mock参数,mock=force:return+null 强制所有服务调用返回null, mock=fail:return+null 则还会走远程调用,失败才会返回null。mock=throw,不会远程调用,直接抛出RpcException。

路由:过滤Directory获取的所有Invoker列表,比如设置只能调用某个ip的服务

路由分类:条件路由,文件路由,脚本路由

负载均衡:

容错策略中的负载均衡都使用抽象父类的AbstractClusterInvoker中定义的invoker<T> select方法,在抽象父类LoadBalance的基础上封装了一些新的特性,在负载均衡之前经过三个步骤检测:

1. 粘滞连接。用于有状态的服务,尽可能让客户端总是调用同一个提供者。<dubbo:protocol name="dubbo" sticky="true"/>

2.可用检测。调用前检测节点是否可用。默认开启。URL中如果含有cluster.availablecheck=false 则不会检测

3.避免重复调用。对于已经调用过的远程服务,避免重复调用。

兜底:在所有可用的重复调用过的服务选择出来做负载均衡

负载均衡算法:

1.Random:随机,按权重设置随机概念。默认

2.RoundRobin:轮询,权重轮询。普通权重轮询问题:会造成某个节点会突然被频繁选中,容易造成一个节点流量暴增。最新Dubbo采用了Nginx的平滑轮询算法。在轮询是会穿插调用其他节点。

3.LeastActive:最少活跃调用数。活跃数相同则随机,基于调用前后的计数差。使慢的提供者收到更少的请求,因为慢的计数差更大。

4.ConsistentHash:一致性Hash,相同的参数总是发到同一个提供者,基于虚拟节点,默认是160份。默认只对第一个参数做Hash。

第8章 Dubbo扩展点

微内核,富生态。扩展功能强大。

RPC层扩展点

Proxy层:有JDK,CGLLIB,Javassist等,默认是Javassist(基于性能和使用简易性考虑)。可以使用proxy参数指定

Registry层:check=false则连接不会检测,否则在断开连接时抛出异常。支持file参数设置本地文件缓存,timeout参数设置请求的超时时间,session指定连接超时和过期超时。用protocol参数指定key,扩展key名有zookeeper,redis,multicast,dubbo

Cluster扩展点:Cluster扩展点:有fastover,failsafe,failback,默认是failover。RouterFactory扩展点:file,script,condition。LoadBalance扩展点:random,roundrobin,leastactive,consisitenthash。ConfiguratorFatory扩展点

Remote层扩展点

Protocol扩展点:injvm,dubbo,rmi,http,hessian,rest,thrift

Filter扩展点:在Invoker调用前后执行自定义逻辑。

ExporterListener/InvokerListener扩展点:ExporterListener暴露和取消暴露服务时提供回调,InvokerListener服务引用于销毁引用时提供回调。

Exchange扩展点:封装请求/响应模式,上层业务关注的。

Transport层扩展点:Transport扩展点,屏蔽底层通信框架接口,使用统一的通信接口。有mina,netty3,netty4,netty,grizzly。Dispatcher,由于有些请求处理慢,用来派发请求到不同的线程池处理。Codec2扩展点:对数据编码和解码。ThreadPool扩展接口,Dispatcher派发到这些线程池处理。

Seralize:对象序列化。有fastjson,fst,hessian2,java,protostuff,默认是hessian2.

第9章 Dubbo高级特性

1. 异步调用

2.泛化调用。在服务端构造出新的RpcInvocation对象发起调用。

3.结果缓存。使用JSON.toJSONString将请求参数转换成字符串,然后拼接唯一的key,用于缓存唯一键。<dubbo:reference cache="lru" .../>,默认最近最少使用策略,缓存1000个结果,超过会删除头节点数据。

4.Mock调用。服务容错降级,只有RpcException才会触发。默认是查找类名加Mock的类做容错处理。当然可以自定义指定容错类。有6种方法使用Mock能力

5.参数回调:服务端异步调用客户端方法,客户端会自动暴露一个服务

6.服务分组和版本。实现业务资源隔离,防止整体资源被个别调用放拖垮。也可以验证不同版本的功能。

7.上下文信息。每发起RPC调用上下文会改变。

第10章 Dubbo过滤器

每次调用都会执行,对性能有影响。

内置过滤器使用了@Activate默认会被激活,通过group来区分客户端和服务器端过滤器

过滤器顺序:过滤器用了装饰器模式,像俄罗斯套娃一样,把过滤器一个又一个套到Invoker上。

服务端过滤器:

1.AccessLogFilter:可以用accesslog参数开启日志打印,可以写到文件,但是日志是用Set保存的,所以是无序的。

2.ExecuteLimitFilter:限制每个服务中每个方法的最大并发数。通过executes=“10”参数设置,默认不做限制,设置小于等于0也不做设置。实现原理是用一个ConcurrentMap缓存并发计数器,每个URL生成一个key,value是RpcStatus对象用来做计数。调用之前获取RpcStatus对象并做+1,在finally做-1。如果再+1的时候发现超过最大并发数则抛出异常。

3.ClassLoaderFilter:使用调用者的类加载器去实例化调用者的相关实例,用于那些反双亲委派的场景。原理是把调用者的类加载器保存到上下文,调用完后恢复上下文的加载器。

4.ContextFilter:设置每次调用的上下文。

5.ExceptionFilter:防止异常在消费端序列化失败,把一些异常序列化为字符串,并包装为一个RuntimeException返回到客户端。

6.TimeoutFilter:日志类型过滤器。如果发起调用超时,则会打印一个警告日志,并不会干扰业务的正常运行。

7.TokenFilter:某些服务端不想让消费端绕过注册中心直连自己,消费端必须从服务器获得token。服务端通过token参数设置token令牌。

8.TpsLimitFilter:限流。不是默认启动,需要手动设置

<dubbo:parameter key="tps" value="1000" /> 每次发放 1000个令牌

令牌刷新的间隔是1秒,如果不配置,则默认是60秒

<dubbo:parameter key="tps.interval" value="1000" />

内部使用一个ConcurrentMap缓存接口的令牌数,key是interface+group+version,value是一个StatItem,包装了令牌的刷新间隔,每次发放的令牌数等属性。

消费端过滤器:

1.ActiveLimitFilter:限制客户端的并发数,与ExecuteLimitFilter类似,但是超过并发数时不是直接抛出异常,而是等到超时(timeout属性)才会抛异常。

2.ConsumerContextFilter:与ContextFilte配合使用

3.DeprecaateFilter:如果配置了deprecaated=true,则会打印一段ERROR级别的日志

4.FutureFilter:在调用前后出现异常时,触发调用用户配置的回调方法。

第11章 Dubbo注册中心扩展实践

etcd:分布式键值,可靠集群存储数据,使用了Raft算法保证集群数据一致性,可以读写数据,数据可以被监听,变化时通知监听者。广泛应用于Kubernates

相比zookeeper优点:

1. 不需要每次子节点变更都重新拉取节点所有数据,大大降低网络压力。

2. 使用增量快照,避免在创建快照时暂停。

3. watch可以一直存在(zk是要多次注册的)

第13章 Dubbo未来展望

dubbo未来发展:

1. 符合主流开发习惯:支持REST和Spring Boot

2. 降低开发和运维成本:上云

3. 全齐短板:完善服务化治理方案。

Dubbo2.7.X新特性:

1. JDK8特性引入和repackage : 使用default method,不需要用抽象类来定义默认方法了。使用CompletedFuture,future结束后直接触发回调。还有Optional,Lambda表达式,function等

2.异步化支持,老的异步直接调用会返回null,需要通过上下文来获取future,然后阻塞等待结果。新的直接使用了CompletableFuture。有直接接口定义和注解(@AsyncFor)。

3.元数据管理优化。老的有大量元数据需要传递到注册中心,当更新是会全量拉取,如果集群规模较大,整个网络传输量会激增。新的对元数据做了简化,只传递少量的数据到注册中心,额外元数据保存在redis。另外可以使用etcd做增量更新。

4.动态配置管理。一个注册中心分成了:注册、配置、元数据(注册中心不需要的参数数据)三个中心。

配置优先级(靠前更高优先级):JVM启动参数-D设置--》XML/API配置---》dubbo.properties---》ConfigConter。

默认支持Apollo,Nacos,ZooKepper作为配置中心

5.路由规则管理。以前只支持服务粒度,现在支持应用级别,tag类型,从注册中心转移到配置中心,对应精准的路由规则。

微服务面临的问题:

1.类库内容众多,门槛较高

2.框架升级极其困难

3.应用臃肿

4.多语言支持维护成本高

5.新旧应用共存

6.技术人才的多样性

7.服务治理的缺失

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值