RPC框架Dubbo分析

1,背景
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进
  • 单一应用架构
  • 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本
  • 此时,用于简化增删改查工作量的 数据访问框架(ORM) 是关键
  • 垂直应用架构
  • 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率
  • 此时,用于加速前端页面开发的 Web框架(MVC) 是关键
  • 分布式服务架构
  • 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求
  • 此时,用于提高业务复用及整合的 分布式服务框架(RPC) 是关键
  • 分布式服务RPC框架
  • 按业务线拆分
  • 部署分离
  • 每次发布只部署部分服务器
  • 每个节点可根据不同需求伸缩扩展
  • 每个应用之间更新,部署,运行不影响
    • 团队分离
    • 数据分离
    • 停止RPC滥用,垂直业务内优先通过本地jar调用,跨业务才采用RPC调用
    • 正确的识别业务逻辑的归属,让各个模块最大化内聚,从性能,可用性和维护性上减少耦合
  • 流动计算架构
  • 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率
  • 此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键
2,需求
在大规模服务化之前,应用可能只是通过RMI或Hessian等工具,简单的暴露和引用远程服务,通过配置服务的URL地址进行调用,通过F5等硬件进行负载均衡
  • 当服务越来越多时,服务URL配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大
  • 此时需要一个服务注册中心,动态的注册和发现服务,使服务的位置透明
  • 并通过在消费方获取服务提供方地址列表,实现软负载均衡和Failover,降低对F5硬件负载均衡器的依赖,也能减少部分成本
  • 当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系
  • 这时,需要自动画出应用间的依赖关系图,以帮助架构师理清理关系
  • 接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?
  • 为了解决这些问题,第一步,要将服务现在每天的调用量,响应时间,都统计出来,作为容量规划的参考指标
  • 其次,要可以动态调整权重,在线上,将某台机器的权重一直加大,并在加大的过程中记录响应时间的变化,直到响应时间到达阀值,记录此时的访问量,再以此访问量乘以机器数反推总容量
3,Dubbo架构
  • Dubbo分层
  • 层次结构
  • Business
  • Service
    • RPC
  • Config
  • Proxy
  • Registry
  • Cluster
  • Monitor
  • Protocol
    • Remoting
  • Exchange
  • Transport
  • Serialize
    • 层说明
  • config(配置层 )
  • 对外配置接口
  • 以ServiceConfig, ReferenceConfig为中心,可以直接new配置类,也可以通过spring解析配置生成配置类
    • proxy(服务代理层)
  • 服务接口透明代理,生成服务的客户端Stub和服务器端Skeleton
  • 以ServiceProxy为中心,扩展接口为ProxyFactory
  • 选择
  • Javassist ProxyFactory
  • Jdk ProxyFactory
    • registry( 注册中心层)
  • 封装服务地址的注册与发现
  • 以服务URL为中心,扩展接口为RegistryFactory, Registry, RegistryService
  • 选择
  • Zookeeper
  • 支持基于网络的集群方式,有广泛周边开源产品,建议使用dubbo-2.3.3以上版本(推荐使用)
  • 依赖于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 & SPI
  • Protocol & ProxyFactory & Filter
  • Cluster & Directory & Router & LoadBalance
  • Transporter & Serialization & ThreadPool
  • TelnetHandler & 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
  • Transporter
  • LoadBalance
    • API可配置,一定可编程
  • 配置用于简化常规使用
  • 编程接口用于框架集成
    • API区分命令与查询
  • 命令:无返回值表示命令,有副作用
  • 查询:有返回值表示查询,保持幂等,无副作用
  • 组件协作原则
  • 管道 v.s. 派发
  • 管道
  • 组合行为
  • 主功能以截面实现
  • 比如:Servlet
    • 派发
  • 策略行为
  • 主功能以事件实现
  • 比如: Swing
    • 分布 v.s. 共享
  • 分布
  • 在行为交互为主的系统是适用
  • 状态通过行为传递
    • 共享
  • 在以管理状态为主的系统中适用
  • 状态通过仓库共享
    • 主过程拦截
  • Web框架的请求响应流
  • ORM框架的SQL执行
  • Service框架的调用过程
  • 反例:IBatis2在SQL执行过程中没有设拦截点,导致添加安全或日志拦截,执行前修改分页SQL等,不得不hack源代码
    • 事件派发
  • 过程
  • 执行前后
  • 触发附带非关键行为
    • 状态
  • 值的变化
  • 触发状态观察者行为
    • 关键路径
  • 关键路径
  • 采用拦截链分离职责
  • 保持截面功能单一,不易出问题
    • 非关键路径
  • 采用后置事件派发
  • 确保派发失败,不影响主过程运行
    • 协作防御
  • 可靠性分离
  • 可靠操作
  • 不可靠操作 (尽量缩小)
    • 状态分离
  • 无状态
  • 有状态 (尽量缩小)
  • 不可变类 (尽量final)
    • 状态验证
  • 尽早失败
  • 前置断言 + 后置断言 + 不变式
    • 异常防御,但不忽略异常
  • 异常信息给出解决方案
  • 日志信息包含环境信息
    • 降低修改时的误解性,不埋雷
  • 避免基于异常类型的分支流程
  • 保持null和empty语义一致
  • 功能演进原则
  • 开闭原则
  • 对扩展开放
  • 对修改关闭
  • 软件质量的下降,来源于修改
  • 替换整个实现类,而不是修改其中的某行
    • 增量式 v.s.扩充式
  • Dubbo增量式扩展
  • Remoting
  • Transport:
  • 单向消息发送,抽象Mina/Netty
    • Exchange:
  • 封装Request-Respose语义
  • 调用两次单向消息发送完成
    • RPC
  • Portocol:
  • 协议实现,不透明,点对点
    • Cluster:
  • 将集群中多个提供者伪装成一个
    • Proxy:
  • 透明化接口,桥接动态代理
    • 在高阶附加功能
  • 尽可能少的依赖低阶契约,用最少的抽象概念实现功能
  • 当低阶切换实现时,高阶功能可以继续复用
7,Dubbo编码约定
  • 异常和日志:
  • 尽可能携带完整的上下文信息,比如出错原因,出错的机器地址,调用对方的地址,连的注册中心地址,使用Dubbo的版本等。
  • 尽量将直接原因写在最前面,所有上下文信息,在原因后用键值对显示。
  • 抛出异常的地方不用打印日志,由最终处理异常者决定打印日志的级别,吃掉异常必需打印日志。
  • 打印ERROR日志表示需要报警,打印WARN日志表示可以自动恢复,打印INFO表示正常信息或完全不影响运行。
  • 建议应用方在监控中心配置ERROR日志实时报警,WARN日志每周汇总发送通知。
  • RpcException是Dubbo对外的唯一异常类型,所有内部异常,如果要抛出给用户,必须转为RpcException。
  • RpcException不能有子类型,所有类型信息用ErrorCode标识,以便保持兼容。
  • 配置和URL:
  • 配置对象属性首字母小写,多个单词用驼峰命名(Java约定)。
  • 配置属性全部用小写,多个单词用"-"号分隔(Spring约定)。
  • URL参数全部用小写,多个单词用"."号分隔(Dubbo约定)。
  • 尽可能用URL传参,不要自定义Map或其它上下文格式,配置信息也转成URL格式使用。
  • 尽量减少URL嵌套,保持URL的简洁性。
  • 单元和集成测试:
  • 单元测试统一用JUnit和EasyMock,集成测试用TestNG,数据库测试用DBUnit。
  • 保持单元测试用例的运行速度,不要将性能和大的集成用例放在单元测试中。
  • 保持单元测试的每个用例都用try...finally或tearDown释放资源。
  • 减少while循环等待结果的测试用例,对定时器和网络的测试,用以将定时器中的逻辑抽为方法测试。
  • 对于容错行为的测试,比如failsafe的测试,统一用LogUtil断言日志输出。
  • 扩展点基类与AOP:
  • AOP类都命名为XxxWrapper,基类都命名为AbstractXxx。
  • 扩展点之间的组合将关系由AOP完成,ExtensionLoader只负载加载扩展点,包括AOP扩展。
  • 尽量采用IoC注入扩展点之间的依赖,不要直接依赖ExtensionLoader的工厂方法。
  • 尽量采用AOP实现扩展点的通用行为,而不要用基类,比如负载均衡之前的isAvailable检查,它是独立于负载均衡之外的,不需要检查的是URL参数关闭。
  • 对多种相似类型的抽象,用基类实现,比如RMI,Hessian等第三方协议都已生成了接口代理,只需将将接口代理转成Invoker即可完成桥接,它们可以用公共基类实现此逻辑。
  • 基类也是SPI的一部分,每个扩展点都应该有方便使用的基类支持。
  • 模块与分包:
  • 基于复用度分包,总是一起使用的放在同一包下,将接口和基类分成独立模块,大的实现也使用独立模块。
  • 所有接口都放在模块的根包下,基类放在support子包下,不同实现用放在以扩展点名字命名的子包下。
  • 尽量保持子包依赖父包,而不要反向。
8,Dubbo高性能之道
  • 参考文章“【总结】Netty(RPC高性能之道)原理剖析 ”

转载于:https://www.cnblogs.com/yangqiong1989/p/6882470.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/372126.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

定时器、计时器。

//第一种 每一秒执行一次(重复性)double delayInSeconds 1.0;timer dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));dispatch_source_set_timer(timer, DISPATCH_TIME_NO…

mybatis oracle trim,Mybatis trim标签

trim代替where/set标签trim是更灵活用来去处多余关键字的标签,它可以用来实现where和set的效果。SELECT *FROM user uu.username LIKE CONCAT(CONCAT(%, #{username, jdbcTypeVARCHAR}),%)AND u.sex #{sex, jdbcTypeINTEGER}AND u.birthday #{birthday, jdbcTypeD…

自己初学时的随笔记录

如果富文本编辑器 jsp....文件可以找到但是就是显示不出来,可能是Controller控制器中RequestMapping后边没有写路径 ---------------------------------------------------------------------------------------------------------------------------- iframe框架内…

终极JPA查询和技巧列表–第1部分

我们可以在Internet上找到一些JPA“如何做”,在本博客的此处,教您如何使用JPA执行多项任务。 通常,我看到有人问有关使用JPA进行查询的问题。 通常,为了回答此类问题,提供了几个链接,以尝试找到该问题的解决…

请求重定向(网上抄录)

抄录地址 http://www.sosuo8.com/article/show.asp?id1158 (1)Server.Transfer方法: Server.Transfer("m2.aspx");//页面转向(服务器上执行). 服务器停止解析本页,保存此页转向前的数据后,再使页面转向到m2.aspx, 并将转向前数据加上m2.aspx页结果返回给浏览器. (…

oracle走当前时间分区,Oracle分区使用波斯日历的时间间隔

与数据库级NLS_CALENDAR相比,没有其他方法可以在不同的日历中定义间隔。通过使用虚拟列划分每个日期落入的(波斯)月份的数字表示,可以得到相同的效果:create table test_temp_times (id number(18) not null,xdate date not null,str varchar…

Spring集成–第2节–更多世界

这是Spring Integration Session 1的后续活动 第一部分是使用Spring Integration的简单Hello World应用程序。 我想通过考虑其他一些方案来进一步介绍它。 因此,对Hello World应用程序的第一个更改是添加网关组件。 要快速重新访问较早的测试程序,请执行…

Python中Dict的查找

Dict的类型的查找使用的是lookdict函数 static PyDictKeyEntry * lookdict(PyDictObject *mp, PyObject *key,Py_hash_t hash, PyObject ***value_addr) 函数的参数中,*value_addr是指向匹配slot中值的指针。 这个函数在正确的情况下一定会返回一个指向slot的指针&a…

文字特效代码大全

代码收集来源于网络博友,感谢博友提供,本人只收集,整理,说明. 1.删除线:<FONT style"TEXT-DECORATION: line-through">写上你想写的字</FONT> 效果如下 写上你想写的字 2.文字顶部加横线:<font style"text-decoration:overline">写上你想…

oracle 会话实例,返璞归真:Oracle实例级别和会话级别的参数设置辨析

杨廷琨(yangtingkun)云和恩墨 CTO高级咨询顾问&#xff0c;Oracle ACE 总监&#xff0c;ITPUB Oracle 数据库管理版版主参数文件是Oracle数据库文件中级别最低&#xff0c;也是最基本的文件&#xff0c;但是也是数据库实例启动第一个涉及的文件。如果参数文件缺失或者某些参数设…

ExtJs CheckboxSelectionModel 全选操作后 清空表格头的checkBox

关键代码&#xff1a; var hd Ext.getCmp("interviewSubscriptionGrid").getEl().select(div.x-grid3-hd-checker).first(); if (hd.hasClass(x-grid3-hd-checker-on)) { hd.removeClass(x-grid3-hd-checker-on); } 转自&#xff1a;ExtJs Checkbox…

在多节点集群中运行Cassandra

这篇文章收集了我在多节点中设置Apache Cassandra集群的步骤。 在设置集群时&#xff0c;我已经参考了Cassandra Wiki和Datastax文档。 详细介绍了以下过程&#xff0c;分享了我建立群集的经验。 设置第一个节点 添加其他节点 监视集群– nodetool &#xff0c; jConsole &am…

Oracle 添加 scott 示例用户

学习SQL有一段时间了&#xff0c;但是也忘记的差不多了&#xff0c;今天有赶紧复习复习&#xff0c;然后发现一个问题&#xff0c;为啥之前看的视频教程&#xff0c;马士兵用的Oracle有scott用户和那些表格&#xff0c;而我的没有&#xff1f;难道是Oracle取消了&#xff1f;然…

win8oracle10g安装报错,Win8电脑安装Oracle 10g提示程序异常终止的解决方法

有win8系统用户反映说在安装Oracle 10g的时候&#xff0c;选择高级安装之后&#xff0c;就弹出一个窗口&#xff0c;提示程序异常终止&#xff0c;发生内部错误&#xff0c;导致Oracle 10g安装失败&#xff0c;该怎么解决这样的问题呢&#xff1f;下面随小编一起来看看Win8电脑…

MFC的消息循环

MFC的消息循环 消息分为队列消息(进入线程的消息队列)和非队列消息(不进入线程的消息队列)。对于队列消息&#xff0c;最常见的是鼠标和键盘触发的消息&#xff0c;例如WM_MOUSERMOVE,WM_CHAR等消息&#xff1b;还有例如&#xff1a;WM_PAINT、WM_TIMER和WM_QUIT。当鼠标、键…

<avatar: frontiers of pandora>技术overview

https://www.eurogamer.net/digitalfoundry-2023-avatar-frontiers-of-pandora-and-snowdrop-the-big-developer-tech-interview https://www.youtube.com/watch?vLRI_qgVSwMY&t394s 主要来自euro gamer上digital foundry对于avatar的开发团队Massive工作室的采访&#xf…

使用Hibernate 4,JPA和Maven的架构创建脚本

这种情况很简单–您想要在构建应用程序时生成数据库模式创建脚本&#xff08;然后在目标数据库上执行脚本&#xff09;&#xff0c;这对于Hibernate 3来说相对容易&#xff0c;因为有 hibernate3-maven-plugin &#xff0c;但是与Hibernate 4不兼容。当然&#xff0c;对于每个新…

iOS 启动连续闪退保护方案

版权声明&#xff1a;本文由刘笑江原创文章&#xff0c;转载请注明出处: 文章原文链接&#xff1a;https://www.qcloud.com/community/article/79 来源&#xff1a;腾云阁 https://www.qcloud.com/community 一.引言 “如果某个实体表现出以下任何一种特性&#xff0c;它就具备…

实战Java内存泄漏问题分析 -- hazelcast2.0.3使用时内存泄漏 -- 2

hazelcast 提供了3中方法调用startCleanup:第一种是在ConcuurentMapManager的构造函数中&#xff0c;通过调用node的executorManager中的ScheduledExecutorService来创建每秒运行一次cleanup操作的线程&#xff08;代码例如以下&#xff09;。因为这是ConcuurentMapManager构造…

oracle 11203 ora32701,11G RAC ORA-32701 参考学习

节点1&#xff1a;Wed Feb 13 16:08:06 2019Errors in file /u01/app/oracle/diag/rdbms/testdb/testdb1/trace/testdb1_dia0_9267.trc (incident1248083):ORA-32701: Possible hangs up to hang ID4 detectedIncident details in: /u01/app/oracle/diag/rdbms/testdb/testdb1/…