1. 为什么要做系统拆分?如何进行系统拆分?拆分后不用dubbo可以吗?
为什么要做系统拆分?
- 单体应用的弊端
-
部署成本高:无论是修改一行代码还是10行代码,都要全量替换
-
改动影响大,风险高:不论是代码改动多少,成本都相同
-
因成本高、风险高,所以导致部署频率低:无法快速交付客户需求
- 微服务架构的特点:
-
针对特定服务发布,影响小、风险小、成本低
-
频繁发布版本,快速交付需求
-
低成本扩容、弹性伸缩、适应云环境
- 微服务带来的问题:
-
分布式系统的复杂性:微服务团队的技术栈水平更高
-
部署、测试和监控的成本问题
-
分布式事务和CAP的相关问题
拆分后的好处
-
每个工程独立的git仓库,避免代码冲突
-
每个码农维护单独的服务,包括开发、测试、发布
-
方便技术升级,只要保持接口不变:面向接口编程?
-
代码量几十万的大中型项目,团队里有几十个人。如果不拆分系统,开发效率极其低下,问题很多。
-
分布式系统拆分之后,可以大幅度提升复杂系统大型团队的开发效率。
如何进行系统拆分?
-
领域驱动模型
-
多次拆分
拆分后不用dubbo可以吗?
dubbo 就是一种rpc框架,就是本地进行接口调用,但是dubbo会代理这个调用请求,跟远程机器网络通信,给你处理掉负载均衡、
服务实例上下线自动感知、超时重试等问题。
分布式系统示意图
2. 说一下 dubbo 的工作原理?注册中心挂了可以继续通信吗?说说一次rpc请求的流程?
-
kafka高可用架构原理、es分布式架构原理、redis线程模型原理、dubbo工作原理
-
系统设计:设计MQ、设计搜索引擎、设计一个缓存、设计rpc框架
dubbo工作原理
第一层:service层:provider和consumer,接口,留给自己实现的
第二层:config层:任何一个框架,都需要提供配置文件,让你进行配置
第三层:proxy层:代理层:无论是consumer还是provider,dubbo都会帮你生成代理,代理之间进行网络通信
第四层:registry层:provider注册自己作为一个服务,consumer就可以到注册中心去寻找自己要调用的服务
第五层:cluster层:provider可以部署在多台机器上,多个provider就组成了一个集群
第六层:monitor层:consumer调用provider,调用了多少次啊?统计信息监控
第七层:protocol层:负责集体的providr和consumer之间调用接口。封装rpc调用
第八层:exchange层:信息交换层,封装请求响应模式,同步转异步
第九层:transport层:网络传输层,抽象mina和netty为统一接口
第十层:serizlize层:数据序列化层
工作流程
-
provider 向注册中心去注册
-
consumer 从注册中心订阅服务,注册中心会通知 consumer 注册好的服务
-
consumer 调用 provider
-
consumer 和 provider 都异步的通知监控中心
注册中心挂了可以继续通信吗?
可以。因为刚开始初始化的时候,消费者将提供者的地址等信息拉取到本地缓存,所以注册中心挂了可以继续通信。
3. dubbo支持哪些通信协议和序列化协议?
image.png- 默认的 dubbo 协议,单一长连接,NIO异步通信,基于 hessian 作为序列化。
-
适用场景:传输数据量很小(单次请求在100kb以内),但是并发量很大
-
消费者上百台,每天调用量达上亿次。每个消费者维持一个长连接,长连接基于NIO异步通信。
-
rmi协议:走 java 二进制序列化,多个短连接,适合消费者和提供者差不多,适用于文件的传输。
-
hessian 协议:走hessian序列化协议
-
http协议:json序列化
-
webservice: SOAP 文本序列化
4. dubbo 负载均衡策略
image.png-
random loadbalance 基于权重的随机算法。默认、可设置权重
-
roundrobin 加权轮询算法。均匀地将流量打到机器上去。调整权重
-
leastactive 最少活跃调用数算法。自动感知,某个机器性能接收的请求越少
-
consistanthash 一致性hash算法:相同参数的请求一定分发到一个provider上去,当provider挂掉的时候,会基于虚拟节点均匀分布剩余的流量,抖动不会太大。
5. dubbo 集群容错策略
-
failover cluster 失败自动切换,自动重试到其他机器。默认。读操作
-
failfast cluster 一次调用失败就立即失败。写操作
-
failsafe cluster 出现异常时忽略掉。不重要的接口调用:记录日志
-
failback cluster 失败了后台自动记录请求,然后定时重发。适合写消息队列
-
forking cluster 并行调用多个provider,只要一个成功就立即返回
-
broadcast cluster 逐个调用所有的 provider
6. 动态代理策略
-
默认使用 javassist 动态字节码生成,创建代理类
-
可以通过 spi 扩展机制配置自己的动态代理策略
7. SPI service provider interface
-
@SPI("dubbo") : 通过 SPI 机制来听实现类,实现类是通过dubbo作为默认key去配置文件中找到的,配置文件名称与接口全限定名一样。
-
如果要动态替换掉默认的实现类,需要使用 @Adaptive 接口。
在protocol接口中,有两个方法加了 @Adaptive 注解,就是说那两个接口会被代理实现。
通过这个URL中的参数不同,就可以控制动态使用不同的组件实现类。 -
dubbo 里面提供了大量类似以上的扩展点。如果要扩展一个东西,只要自己写一个jar,让 consumer 或者是 provider 工程,依赖你的jar,
在你的jar里指定目录下配置好接口名称对应的文件,里面通过key=实现类。
8. 如何基于 dubbo 进行服务治理、服务降级、失败重试以及重试?
服务治理
- 调用链路自动生成:
将各个服务之间的调用自动记录下来,然后自动将各个服务之间的依赖关系和调用链路生成出来,做成一张图,显示出来。
- 服务访问压力以及时长统计:
-
需要自动统计各个接口和服务之间的调用次数以及访问延时,分为两个级别:
-
接口粒度:每个服务的每个接口每天被调用多少次。TP50,TP90,TP99,三个档次的请求延时分别是多少?
-
从源头入口开始,一个完整的请求链路经过几十个服务之后,完成一次请求,每天全链路走多少次,全链路请求延时的 TP50,TP90,TP99 分别是多少?
- 其他的
-
服务分层(避免循环依赖)
-
调用链路失败监控和报警
-
服务鉴权
-
每个服务的可用性的监控(接口调用成功率?几个9?)
服务降级
<dubbo:reference id="appServer" interface="com.gupaoedu.dubbo.IAppInterface" version="1.0.0"
cluster="failover"
timeout="10"
mock="com.DataBaseConnection.dubbo.TestMock"
retries="3"/>
mock:服务接口实现类
失败重试和超时重试
-
如果超时了, timeout 就会设置超时时间。200ms
-
如果是调用失败了就会重试指定的次数
-
retries = 3 。读请求,如果第一次没读到,报错。重试指定的次数,尝试再读取两次。
9. 分布式服务接口的幂等性如何设计?(比如不能重复扣款)
image.png-
每个请求必须有唯一的标识:订单ID
-
每次处理完请求之后,必须有一个记录标识这个请求处理过了:用mysql中记录个状态,支付之前记录一条这个订单的支付流水。
-
每次接收请求需要进行判断之前是否处理过的逻辑处理。
如果有一个订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,order已经存在了,
唯一键约束生效,报错插不进去的。 -
redis 支付订单之前,先插入一条支付流水,order_id 建一个唯一键。重复请求过来时,先查下 order_id 是否有值,有就不需要再重复了。
10. 分布式接口的顺序性如何保证?
- 用 dubbo 的一致性 hash 负载均衡策略,将某一个订单 ID 对应的请求都分发到某个机器上去,接着就是在那个机器上因为
可能还是多线程并发执行的,得立即将某个订单对应的请求扔到一个内存队列中,强制排队,来保证顺序性。
- 造成的问题:机器热点
- 合并多个操作为一个操作。
11. 如何自己设计一个类似 dubbo 的 rpc 框架?
-
注册中心
-
消费者需要到注册中心拿到对应的服务信息
-
发请求,基于动态代理:面向接口获取一个动态代理,这个动态代理就是接口在本地的一个代理,然后这个代理会找到服务对应的机器地址
-
发送到哪个服务上去?负载均衡
-
找到一台机器,怎么发送? netty、nio方式。发送啥格式数据? hessian 序列化协议
-
服务器端需要针对你自己的服务生成一个动态代理,监听某个网络端口了,然后代理你本地的服务代码。接收到请求时,就调用对应的服务代码。
12. dubbo 底层架构原理
13. dubbo 底层的网络通信机制原理
14. dubbo 框架从架构设计角度,是怎么保证极高的可扩展性的?
-
核心的组件全部接口化,组件和组件之间的调用,必须是依托于接口,去动态找配置的实现类,如果没有配置就用它自己默认的。
-
提供一种自己实现的组件的配置方式。比如自己实现了某个组件,配置一下,人家到时候运行时直接找配置的组件。
15. 设计一个 rpc 框架,网络通信、代理机制、负载均衡等该如何设计?
-
动态代理:rpc 框架的一切的逻辑细节,都是在这个动态代理中实现的,动态代理里面的代码逻辑是 rpc 框架核心的逻辑。
-
服务注册中心,使用什么技术实现?
-
手写。类似于map的数据结构。服务去注册,其他服务去拉取注册表进行发现。
-
zookeeper:服务注册表的数据结构
-
cluster层,从本地缓存的服务注册列表里获取到要调用的服务机器列表
-
负载均衡:注册的服务列表中选择一个机器返回
-
数据组织:协议、序列化(复杂的请求数据序列化为二进制数组)机制。底层用什么网络通信框架(netty、mina)
参考代码
https://github.com/ElegantResearcher/learn/tree/master/rpc
网友评论