分布式与RPC
软件架构
单一应用架构
-
即将所有的功能都部署到同一个服务器中
-
优点
- 简单易用 开发难度低 适合用于规模流量小的网站
-
缺点
-
扩展困难
-
不利于多人同时开发 升级维护
-
系统的空间占用大
-
分布式服务架构
- 即将核心业务抽取出来 形成一个独立的服务
分布式系统
-
即将服务分散部署到若干台计算机服务器上
-
RPC(Remote Procedure Call)为远程过程调用
dubbo
概述
-
是一个分布式服务框架 是一个高性能轻量级的Java RPC框架
-
主要功能
-
面向接口的远程方法调用
-
智能容错与负载均衡
-
服务自动注册与发现
-
基本架构
-
说明
-
服务容器Container负责启动、加载、运行服务提供者Provider
-
Provider在启动时向注册中心Registry注册自己所提供的服务
-
服务消费者Consumer在启动时向Registry订阅自己所需的服务
-
Registry返回Provider的地址列表给Consumer 若有变更 Registry将基于长连接推送变更数据给Consumer
-
Consumer从Provider的地址列表中基于软负载均衡算法选一台Provider进行调用 若调用失败 再选另外一台Provider进行调用
-
Provider与Consumer在内存中会累计调用次数与调用时间并每分钟定时的发送一次统计数据到监控中心Monitor
-
-
dubbo支持多种协议 但官方推荐使用dubbo协议 端口号默认为20880
<dubbo:protocol name="dubbo" port="20880" />
直连方式
-
即消费者直接通过一不变的url来访问固定的提供者
<dubbo:reference url="dubbo://localhost:20880" />
术语
-
分包
- 即将服务接口、服务模型(实体bean)、服务异常等都放到公共包中以便提供者与消费者调用
-
粒度
-
服务接口要尽可能的大粒度 即每个服务方法应代表一个功能而不是某个功能的某个步骤 否则将面临分布式事务的问题 且dubbo暂未提供分布式事务的支持
-
服务接口应以业务场景为单位来划分 并对相似业务进行抽象 以防止接口数量爆炸
-
不建议使用过于抽象的接口 如Map query(Map map) 这样的接口没有明确的语义 会给后期的维护带来诸多不便
-
-
版本
- 每个提供者、消费者都应定义版本号 为后续的不兼容升级提供便利
- 变更服务版本:先升级一半的提供者为新版本 再升级全部的消费者 最后再升级剩余的提供者
- 不管一个服务接口是否有多个实现类 只要提供者定义了版本号 则消费者在引用时也必须要指定版本号
dubbo常用标签
-
公用标签
<!-- 服务名称 是dubbo内部使用服务的唯一标识 --> <!-- 必须保证服务名称的唯一性 --> <dubbo:application name="服务名称"/>
<!-- 注册中心 --> <dubbo:registry address="注册中心名称://注册中心所在的ip:注册中心的port" check="是否检查"/>
-
提供者标签
<!-- 暴露服务接口 --> <!-- registry表示注册中心 若不使用注册中心则设值为N/A --> <dubbo:service interface="服务接口名称" ref="此服务接口的实现类在spring容器中的id" registry="N/A" retries="重试次数" timeout="默认为0" version="版本号"/>
-
消费者标签
<!-- 引用远程服务接口 --> <dubbo:reference id="id" interface="服务接口名称" url="dubbo://localhost:20880" registry="N/A" check="是否检查" retries="重试次数" timeout="默认为0" version="版本号"/>
dubbo配置
-
配置原则
- 在提供者处配置参数而不是在消费者处 因为服务者更加了解某个服务的各种参数
-
关闭检查
-
dubbo默认会在启动时检查依赖的服务是否可用 不可用时会抛出异常以阻止spring的初始化 使得项目在上线时能够尽早的发现问题
-
但在测试时 可能会出现循环依赖(即必须有一方服务先启动)/对某些服务不关心 则此时需要关闭检查
-
关闭消费者的某个服务的启动时检查
<dubbo:reference check="false"/>
-
dubbo默认启动服务时检查注册中心是否存在、是否正在运行 若存在注册中心但未运行则会报错
-
关闭注册中心的启动时检查
<dubbo:registry check="false"/>
-
-
重试次数
-
当消费者访问提供者时若访问失败 则会重试访问其它的服务器
-
但重试会带来更长的延迟 使得访问时间变长 降低用户体验
<!-- 重试次数为2(不含第1次访问) --> <dubbo:service retries="2"/> <dubbo:reference retries="2"/>
-
-
超时时间
-
当网络/服务端有问题时会出现一种不确定的中间状态(即超时) 而为了避免因超时而导致的客户端资源耗尽/线程挂起 应设置超时时间
<dubbo:service timeout="10000"/> <dubbo:reference timeout="10000"/>
-
-
版本号
-
同上
<dubbo:service version="1.0.0"/> <dubbo:service version="2.0.0"/> <!-- 注意 有version时不能写url 否则会报错Not found exported service: com.service.UserService:2.0.0:20880 in [com.service.UserService:1.0.0:20880, com.service.UserService2:2.0.0:20880] --> <dubbo:reference version="2.0.0"/>
-
Zookeeper
注册中心概述
- 注册中心会通过特定的协议以统一管理服务 有效的优化了内部应用对服务的发布/使用的流程与管理
- dubbo支持多种注册中心 如Multicast、Simple、Redis、Zookeeper等 但推荐使用Zookeeper
Zookeeper概述
-
是高性能的、分布式的、开源的
-
是高可用的(High Availability)、健壮的
- 指一个系统经过专门的设计减少了不能提供服务的时间从而保持其服务的高度可用性
- 即使zk宕机了 但正在运行中的dubbo服务仍然可以正常访问
- 注册中心宕掉仍然能通过缓存提供服务列表查询 只是不能注册新服务(监控中心宕掉不影响使用 只是会丢失掉部分数据)
- 多个提供者有一台宕掉后不影响使用 但全部宕掉后消费者无法使用服务 且默认会无限次重连(timeout默认为0)以等待提供者恢复
-
是一个与标准文件系统相似的树形结构 zk树中每个节点被称为znode 且zk树中的每个节点都可以拥有子节点 每个节点表示一个服务资源
-
修改conf目录下的zoo_sample.cfg文件
#单位ms 即在服务端之间/服务端与客户端之间每隔2s发送一次信息以表明仍然存活 tickTime=2000 #用来存放zk的数据/文件 建议改为data #dataDir=/tmp/zookeeper dataDir=D:/apache-zookeeper-3.8.0-bin/data #zk对外的服务端口 clientPort=2181 #加上这句 因为zk默认占用8080 admin.serverPort=8888
dubbo-zk
-
只需要在提供者与消费者的原dubbo配置文件中加上
<!-- 使用zk注册中心 --> <dubbo:registry address="zookeeper://localhost:2181"/>
即可
-
启动服务时先启动zk(bin\zkServer.cmd)(Linux为bin\zkServer.sh)(启动时会读取zoo.cfg文件:
Reading configuration from: D:\apache-zookeeper-3.8.0-bin\bin\..\conf\zoo.cfg
) 再启动提供者 最后启动消费者
监控中心
dubbo-admin
-
使用dubbo其实只需要有提供者、消费者、注册中心即可 但这样并不能看到有哪些提供者、消费者 因此为了更好的调试、发现并解决问题 于是引入了dubbo-admin
-
监控中心可以管理提供者、消费者 还可以动态调整dubbo的部署与管理服务
-
对于application.properties文件
#dubbo-admin默认占用的端口号 server.port=7001 #root用户的密码 用来登录监控中心的控制台 spring.root.password=root #注册中心是监控中心的数据来源 dubbo.registry.address=zookeeper://127.0.0.1:2181
-
启动服务时先启动注册中心 再启动提供者 然后启动监控中心(cmd中java -jar dubbo-admin-0.0.1-SNAPSHOT.jar) 最后地址栏上填http://localhost:7001以访问监控中心的控制台(消费者启不启动都可以)