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协议