《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!
依赖于Zookeeper的稳定性
Redis
支持基于客户端双写的集群方式,性能高
要求服务器时间同步,用于检查心跳过期脏数据
Multicast
去中心化,不需要安装注册中心
依赖于网络拓普和路由,跨机房有风险
Simple
Dogfooding,注册中心本身也是一个标准的RPC服务
没有集群支持,可能单点故障
cluster( 路由层)
封装多个提供者的路由及负载均衡,并桥接注册中心
以Invoker为中心,扩展接口为Cluster, Directory, Router, LoadBalance
Cluster选择
Failover
失败自动切换,当出现失败,重试其它服务器,通常用于读操作(推荐使用)
重试会带来更长延迟
Failfast
快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作
如果有机器正在重启,可能会出现调用失败
Failsafe
失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作
调用信息丢失
Failback
失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作
不可靠,重启丢失
Forking
并行调用多个服务器,只要一个成功即返回,通常用于实时性要求较高的读操作
需要浪费更多服务资源
Broadcast
广播调用所有提供者,逐个调用,任意一台报错则报错,通常用于更新提供方本地状态
速度慢,任意一台报错则报错
Router选择
Random
随机,按权重设置随机概率(推荐使用)
在一个截面上碰撞的概率高,重试时,可能出现瞬间压力不均
RoundRobin
轮循,按公约后的权重设置轮循比率
存在慢的机器累积请求问题,极端情况可能产生雪崩
LeastActive
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差,使慢的机器收到更少请求
不支持权重,在容量规划时,不能通过权重把压力导向一台机器压测容量
ConsistentHash
一致性Hash,相同参数的请求总是发到同一提供者,当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动
压力分摊不均
路由规则
条件路由
基于条件表达式的路由规则,功能简单易用
有些复杂多分支条件情况,规则很难描述
脚本路由
基于脚本引擎的路由规则,功能强大
没有运行沙箱,脚本能力过于强大,可能成为后门
容器
Spring
自动加载META-INF/spring目录下的所有Spring配置
Jetty
启动一个内嵌Jetty,用于汇报状态
大量访问页面时,会影响服务器的线程和内存
Log4j
自动配置log4j的配置,在多进程启动时,自动给日志文件按进程分目录
用户不能控制log4j的配置,不灵活
monitor( 监控层)
RPC调用次数和调用时间监控
以Statistics为中心,扩展接口为MonitorFactory, Monitor, MonitorService
protocol( 远程调用层)
封装RPC调用
以Invocation, Result为中心,扩展接口为Protocol, Invoker, Exporter
选择
Dubbo协议
采用NIO复用单一长连接,并使用线程池并发处理请求,减少握手和加大并发效率,性能较好(推荐使用)
适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况
Dubbo缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低
Dubbo协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接
为防止被大量连接撑挂,可在服务提供方限制大接收连接数,以实现服务提供方自我保护
在大文件传输时,单一连接会成为瓶颈
总结
连接个数:单连接
连接方式:长连接
传输协议:TCP
传输方式:NIO异步传输
序列化:Hessian二进制序列化
适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。
适用场景:常规远程服务方法调用
Rmi协议
可与原生RMI互操作,基于TCP协议
偶尔会连接失败,需重建Stub
Hessian协议
可与原生Hessian互操作,基于HTTP协议
需hessian.jar支持,http短连接的开销大
Hessian协议用于集成Hessian的服务,Hessian底层采用Http通讯,采用Servlet暴露服务,Dubbo缺省内嵌Jetty作为服务器实现
可以和原生Hessian服务互操作
提供者用Dubbo的Hessian协议暴露服务,消费者直接用标准Hessian接口调用
或者提供方用标准Hessian暴露服务,消费方用Dubbo的Hessian协议调用
基于Hessian的远程调用协议
总结
连接个数:多连接
连接方式:短连接
传输协议:HTTP
传输方式:同步传输
序列化:Hessian二进制序列化
适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件
适用场景:页面传输,文件传输,或与原生hessian服务互操作
约束
参数及返回值需实现Serializable接口
参数及返回值不能自定义实现List, Map, Number, Date, Calendar等接口,只能用JDK自带的实现,因为hessian会做特殊处理,自定义实现类中的属性值都会丢失
exchange( 信息交换层)
封装请求响应模式,同步转异步
以Request, Response为中心,扩展接口为Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
transport( 网络传输层)
抽象mina和netty为统一接口
以Message为中心,扩展接口为Channel, Transporter, Client, Server, Codec
选择
Netty
性能较好(推荐使用)
一次请求派发两种事件,需屏蔽无用事件
Mina
老牌NIO框架,稳定
待发送消息队列派发不及时,大压力下,会出现FullGC
Grizzly
Sun的NIO框架,应用于GlassFish服务器中
线程池不可扩展,Filter不能拦截下一Filter
serialize( 数据序列化层)
可复用的一些工具
扩展接口为Serialization, ObjectInput, ObjectOutput, ThreadPool
选择
Hessian
性能较好,多语言支持(推荐使用)
Hessian的各版本兼容性不好,可能和应用使用的Hessian冲突,Dubbo内嵌了hessian3.2.1的源码
Dubbo
通过不传送POJO的类元信息,在大量POJO传输时,性能较好
当参数对象增加字段时,需外部文件声明
Json
纯文本,可跨语言解析,缺省采用FastJson解析
性能较差
Java
Java原生支持
性能较差
关系说明
在RPC中,Protocol是核心层,也就是只要有Protocol + Invoker + Exporter就可以完成非透明的RPC调用,然后在Invoker的主过程上Filter拦截点。
图中的Consumer和Provider是抽象概念,只是想让看图者更直观的了解哪些类分属于客户端与服务器端,不用Client和Server的原因是Dubbo在很多场景下都使用Provider, Consumer, Registry, Monitor划分逻辑拓普节点,保持统一概念。
而Cluster是外围概念,所以Cluster的目的是将多个Invoker伪装成一个Invoker,这样其它人只要关注Protocol层Invoker即可,加上Cluster或者去掉Cluster对其它层都不会造成影响,因为只有一个提供者时,是不需要Cluster的。
Proxy层封装了所有接口的透明化代理,而在其它层都以Invoker为中心,只有到了暴露给用户使用时,才用Proxy将Invoker转成接口,或将接口实现转成Invoker,也就是去掉Proxy层RPC是可以Run的,只是不那么透明,不那么看起来像调本地服务一样调远程服务。
而Remoting实现是Dubbo协议的实现,如果你选择RMI协议,整个Remoting都不会用上,Remoting内部再划为Transport传输层和Exchange信息交换层,Transport层只负责单向消息传输,是对Mina,Netty,Grizzly的抽象,它也可以扩展UDP传输,而Exchange层是在传输层之上封装了Request-Response语义。
Registry和Monitor实际上不算一层,而是一个独立的节点,只是为了全局概览,用层的方式画在一起
Dubbo模块分包 模块
dubbo-common 公共逻辑模块,包括Util类和通用模型。
dubbo-remoting 远程通讯模块,相当于Dubbo协议的实现,如果RPC用RMI协议则不需要使用此包。
dubbo-rpc 远程调用模块,抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理。
dubbo-cluster 集群模块,将多个服务提供方伪装为一个提供方,包括:负载均衡, 容错,路由等,集群的地址列表可以是静态配置的,也可以是由注册中心下发。
dubbo-registry 注册中心模块,基于注册中心下发地址的集群方式,以及对各种注册中心的抽象。
dubbo-monitor 监控模块,统计服务调用次数,调用时间的,调用链跟踪的服务。
dubbo-config 配置模块,是Dubbo对外的API,用户通过Config使用Dubbo,隐藏Dubbo所有细节。
dubbo-container 容器模块,是一个Standlone的容器,以简单的Main加载Spring启动,因为服务通常不需要Tomcat/JBoss等Web容器的特性,没必要用Web容器去加载服务
与分层的不同点在于
container为服务容器,用于部署运行服务,没有在层中画出。
protocol层和proxy层都放在rpc模块中,这两层是rpc的核心,在不需要集群时(只有一个提供者),可以只使用这两层完成rpc调用。
transport层和exchange层都放在remoting模块中,为rpc调用的通讯基础。
serialize层放在common模块中,以便更大程度复用
模型 Protocol是服务域,它是Invoker暴露和引用的主功能入口,它负责Invoker的生命周期管理Invoker是实体域,它是Dubbo的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起invoke调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现Invocation是会话域,它持有调用过程中的变量,比如方法名,参数等基本原则 采用Microkernel + Plugin模式,Microkernel只负责组将Plugin,Dubbo自身的功能也是通过扩展点实现的,也就是Dubbo的所有功能点都可被用户自定义扩展所替换采用URL作为配置信息的统一格式,所有扩展点都通过传递URL携带配置信息扩展点加载 Dubbo的扩展点加载从JDK标准的SPI(Service Provider Interface)扩展点发现机制加强而来在扩展类的jar包内,放置扩展点配置文件:META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔注意:这里的配置文件是放在你自己的jar包内,不是dubbo本身的jar包内,Dubbo会全ClassPath扫描所有jar包内同名的这个文件,然后进行合并Provider暴露服务的过程 具体服务到Invoker的转换
ServiceConfig:ref对外提供服务实际类
ProxyFactory:getInvoker()
JavassistProxyFactory
JdkProxyFactory
Invoker转换为Exporter
Invoker:AbstractProxyInvoker的实例
Protocal:export()
DubboProtocol
Dubbo协议的Invoker转为Exporter发生在DubboProtocol类的export方法,它主要是打开socket侦听服务,并接收客户端发来的各种请求,通讯细节由Dubbo自己实现
HessianProtocol
InjvmProtocol
它通过Spring或Dubbo或JDK来实现RMI服务,通讯细节这一块由JDK底层来实现,这就省了不少工作量
RmiProtocol
WebServiceProtocol
Export
Consumer消费服务的过程 把远端服务转为Invoker
ReferenceConfig
Protocol:refer()
DubboProtocol
HessianProtocol
InjvmProtocol
RmiProtocol
WebServiceProtocol
把Invoker转为客户端需要的接口
Invoker
DubboInvoker
HessianInvoker
InjvmInvoker
RmiInvoker
WebServiceInvoker
ProxyFactory:getProxy()
JavassistProxyFactory
JdkProxyFactory
ref
过程:首先ReferenceConfig类的init方法调用Protocol的refer方法生成Invoker实例(如上图中的红色部分),这是服务消费的关键。接下来把Invoker转换为客户端需要的接口
无处不在的Invoker 由于Invoker是Dubbo领域模型中非常重要的一个概念,很多设计思路都是向它靠拢服务消费者Invoker
用户代码通过这个proxy调用其对应的Invoker(DubboInvoker、 HessianRpcInvoker、 InjvmInvoker、 RmiInvoker、 WebServiceInvoker中的任何一个),而该Invoker实现了真正的远程服务调用
服务提供者Invoker
被封装成为一个AbstractProxyInvoker实例,并新生成一个Exporter实例。这样当网络通讯层收到一个请求后,会找到对应的Exporter实例,并调用它所对应的AbstractProxyInvoker实例,从而真正调用了服务提供者的代码
线程模型 过程
Proxy
Client
Transporter
Header -> Codec
Body -> Serialization
Server
Dispatcher
ThreadPool
Implementation
Dispatcher
all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等
direct 所有消息都不派发到线程池,全部在IO线程上直接执行
message 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在IO线程上执行
execution 只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在IO线程上执行
connection 在IO线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池
ThreadPool
fixed 固定大小线程池,启动时建立线程,不关闭,一直持有。(缺省)
cached 缓存线程池,空闲一分钟自动删除,需要时重建
limited 可伸缩线程池,但池中的线程数只会增长不会收缩。(为避免收缩时突然来了大流量引起的性能问题)
4,增强功能
并发控制
连接控制: 连接数控制
分组聚合: 分组聚合返回值,用于菜单聚合等服务
泛化引用: 泛化调用,无需业务接口类进行远程调用,用于测试平台,开放网关桥接等
异步调用
延迟暴露: 延迟暴露服务,用于等待应用加载warmup数据,或等待spring加载完成
延迟连接: 延迟建立连接,调用时建立
隐私传参: 附加参数
5,Dubbo扩展
方法
OSGI
Equinox
Eclipse, HSF
META-INF/MANIFEST.MF
IoC
Spring
META-INF/spring/beans.xml
SPI
java.util.ServiceProvider
JDBC, MessageDigest, ScriptEngine
META-INF/services/com.xx.Xxx
Dubbo SPI Microkernel & SPIProtocol & ProxyFactory & FilterCluster & Directory & Router & LoadBalanceTransporter & Serialization & ThreadPoolTelnetHandler & StatusChecker
6,Dubbo设计原则
模块分包原则
复用度
包中的类应该有同样的重用可能性
紧密协作的类应该放在一个包
对于变化因子,包中的类应全改或全不改
变化应在包内终止,而不传播到其它包
发布的粒度和复用度相同
稳定度
被依赖的包应该总是比依赖者更稳定
不要让一个稳定的包依赖于不稳定包
单向依赖,无环依赖
抽象度
越稳定的包应该越抽象
稳定的包不抽象将导致扩展性极差
抽象的包不稳定将导致其依赖包跟随变化
框架扩展原则 微核 +插件体系
OSGI
IoC
SPI
平等对待第三方
Dogfooding
框架自己的功能也要扩展点实现
甚至微核的加载方式也可以扩展
Autowire
装配逻辑由扩展点之间互助完成
杜绝硬编码的桥接和中间代码
Cascading
层叠扩展粒度,逐级细分
由大的扩展点加载小的扩展点
Law of Demeter
只与×××的扩展点交互,间接转发
保持行为单一,输入输出明确
外置生命周期
API传入参数,SPI扩展点实例
尽量引用外部对象的实例,而不类元
正确:userInstance.xxx()
错误:Class.forName(userClass).newInstance().xxx()
尽量使用IoC注入,减少静态工厂方法调用
正确:setXxx(xxx)
错误:XxxFactory.getXxx(); applicationContext.getBean(“xxx”)
最少化概念模型一致性数据模型
Dubbo统一URL模型
所有配置信息都转换成URL的参数
所有的元信息传输都采用URL
所有接口都可以获取到URL
领域划分原则 服务域
指产品主要功能入口,同时负责实体域和会话域的生命周期管理。
Velocity的Engine
Spring的BeanFactory
实体域
表示你要操作的对象模型,不管什么产品,总有一个核心概念,大家都绕围它转。
Velocity的Template
Spring的Bean
会话域
表示每次操作瞬时状态,操作前创建,操作后销毁。
Velocity的Context
Spring的Invocation
领域模型划分优势
结构清晰,可直接套用
充血模型,实体域带行为
可变与不可变状态分离,可变状态集中
所有领域线程安全,不需要加锁
领域模型线程安全性
服务域
通常服务域是无状态,或者只有启动时初始化不变状态,所以天生线程安全,只需单一实例运行
实体域
通常设计为不变类,所有属性只读,或整个类引用替换,所以是线程安全的
会话域
保持所有可变状态,且会话域只在线程栈内使用,即每次调用都在线程栈内创建实例,调用完即销毁,没有竞争,所以线程安全
接口分离原则 API & SPI
声明式API(Dubbo API):描述需要什么
ServiceConfig
ReferenceConfig
RpcContext
过程式SPI(Dubbo SPI):描述怎么实现
Protocol
一线互联网大厂Java核心面试题库
正逢面试跳槽季,给大家整理了大厂问到的一些面试真题,由于文章长度限制,只给大家展示了部分题目,更多Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等已整理上传,感兴趣的朋友可以看看支持一波!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!
错误:XxxFactory.getXxx(); applicationContext.getBean(“xxx”)
最少化概念模型一致性数据模型
Dubbo统一URL模型
所有配置信息都转换成URL的参数
所有的元信息传输都采用URL
所有接口都可以获取到URL
领域划分原则 服务域
指产品主要功能入口,同时负责实体域和会话域的生命周期管理。
Velocity的Engine
Spring的BeanFactory
实体域
表示你要操作的对象模型,不管什么产品,总有一个核心概念,大家都绕围它转。
Velocity的Template
Spring的Bean
会话域
表示每次操作瞬时状态,操作前创建,操作后销毁。
Velocity的Context
Spring的Invocation
领域模型划分优势
结构清晰,可直接套用
充血模型,实体域带行为
可变与不可变状态分离,可变状态集中
所有领域线程安全,不需要加锁
领域模型线程安全性
服务域
通常服务域是无状态,或者只有启动时初始化不变状态,所以天生线程安全,只需单一实例运行
实体域
通常设计为不变类,所有属性只读,或整个类引用替换,所以是线程安全的
会话域
保持所有可变状态,且会话域只在线程栈内使用,即每次调用都在线程栈内创建实例,调用完即销毁,没有竞争,所以线程安全
接口分离原则 API & SPI
声明式API(Dubbo API):描述需要什么
ServiceConfig
ReferenceConfig
RpcContext
过程式SPI(Dubbo SPI):描述怎么实现
Protocol
一线互联网大厂Java核心面试题库
[外链图片转存中…(img-ySsEMJUx-1714743824035)]
正逢面试跳槽季,给大家整理了大厂问到的一些面试真题,由于文章长度限制,只给大家展示了部分题目,更多Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等已整理上传,感兴趣的朋友可以看看支持一波!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!