文章目录
基础知识
分布式基础理论
什么是分布式系统
-
《分布式系统原理与范型》定义:
- 分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统
-
分布式系统(distributed system)是建立在网络之上的软件系统
-
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进
发展演变
- 分布式服务架构的难度在于如何进行RPC(远程过程调用),以及如何拆分业务,提高业务的复用程度
- 流动计算架构(SOA),基于访问压力实时管理集群容量,提高集群利用率
什么是RPC
-
RPC (Remote Procedure Call) 是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同
-
RPC基本原理图
-
一次完整的RPC调用流程(同步调用)如下:
- 服务消费方 (client) 调用以本地调用方式调用服务
- client stub 接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体 (序列化)
- client stub 找到服务地址,并将消息发送到服务端
- server stub 收到消息后进行解码 (反序列化)
- server stub 根据解码结果调用本地的服务
- 本地服务执行并将结果返回给 server stub
- server stub 将返回结果打包成消息并发送至消费方
- client stub 接收到消息,并进行解码
- 服务消费方得到最终结果
- RPC框架的目标就是将2-8步封装,对用户来说是透明的不可见的
-
决定RPC框架效率的两个因素:通信效率和序列化反序列化效率
-
dubbo的作用:治理和维护各个分系统
dubbo核心概念
- Apache Dubbo 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现
dubbo环境搭建
dubbo-admin搭建
- 下载并启动zookeeper作为注册中心
- 从GitHub上下载dubbo-admin的zip项目
- 解压进入dubbo-admin目录,修改好配置文件后打包,
java -jar jar包名
运行jar包 - 默认
localhost:7001
进入dubbo-admin界面
项目搭建
-
创建2个springboot工程,一个作为提供者,一个作为消费者
-
编写对应工程的功能
-
再编写一个工程作为前二者的接口工程,放入二者的接口以及java bean
-
提供者
-
消费者
-
接口项目
-
测试其他项目的时候其依赖的接口启动类的
@SpringBootApplication
要去掉,因为项目是springboot+dubbo架构,其他项目依赖于接口项目里的pojo,在接口项目中也有启动类@SpringBootApplication
,因为在其他项目的测试类中启动方法时,会加载接口项目的启动类,会造成错误,注释掉接口项目启动类上的@SpringBootApplication
注解即可。但不用测试类的时候又不会出错 -
接口工程不用配置,专注于编写接口,所以接口项目可以使用maven工程创建,这样就彻底解决上述问题了
-
-
在二者的pom.xml中引入依赖的接口
<dependency> <groupId>com.study</groupId> <artifactId>study_interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
-
提供者引入dubbo依赖和zookeeper客户端
<!--引入dubbo--> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.8</version> </dependency> <!--引入zookeeper--> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.12.0</version> </dependency>
-
application.properties
配置dubbo# 当前服务/应用的名字 dubbo.application.name=user-service-provider # 注册中心的协议和地址 dubbo.registry.address=zookeeper://192.168.88.22:2181 # 或 # dubbo.registry.protocol=zookeeper # dubbo.registry.address=192.168.88.22:2181 # 通信规则(通信协议和接口) dubbo.protocol.name=dubbo dubbo.protocol.port=20880 # 连接监控中心 dubbo.monitor.protocol=registry # 开启包扫描,可替代 @EnableDubbo 注解 # dubbo.scan.base-packages=com.study
-
在启动类上加入启动dubbo注解
@SpringBootApplication // 开启基于注解的dubbo功能(主要是包扫描@DubboComponentScan) // 也可以在配置文件中使用dubbo.scan.base-package来替代 @EnableDubbo @EnableDubbo public class UserServiceProvideApplication { public static void main(String[] args) { SpringApplication.run(UserServiceProvideApplication.class, args); } }
-
在提供者需要暴露的服务类上加上注解
@DubboService
@DubboService // 暴露服务 @Component // 理论上这个注解可以不用了,因为在其项目中目前没有需要调用IoC容器中的该对象 public class UserServiceImpl implements UserService { @Override public List<UserAddress> getUserAddressList(String userId) { UserAddress address1 = new UserAddress(1, "翻斗花园二号楼一零零一室", "1", "胡老师", "123", "Y"); UserAddress address2 = new UserAddress(2, "爱情公寓3601", "1", "曾老师", "250", "N"); return Arrays.asList(address1, address2); } }
-
配置消费者:启动类加注解
@EnableDubbo
,配置文件# 避免端口冲突,设为8081端口访问 # server.port=8081 dubbo.application.name=order-service-consumer dubbo.registry.address=zookeeper://192.168.88.22:2181 dubbo.monitor.protocol=registry dubbo.protocol.name=dubbo dubbo.protocol.port=20881
-
在消费者需要消费的接口上加上注解
@DubboReference
@Component public class OrderServiceImpl implements OrderService { @DubboReference // 类似于@Autowired,自动装配需要远程调用的类,项目启动时找不到会报错 private UserService userService; @Override public void initOrder(String userId) { // 查询用户的收获地址 List<UserAddress> addressList = userService.getUserAddressList(userId); addressList.forEach(System.out::println); } }
dubbo配置
配置原则
- JVM 启动 -D 参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口
- XML 次之,如果在 XML 中有配置,则
dubbo.properties
中的相应配置项无效 - Properties 最后,相当于缺省值,只有 XML 没有配置时,
dubbo.properties
的相应配置项才会生效,通常用于共享公共配置,比如应用名
启动时检查
- Dubbo缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止Spring初始化完成,以便上线时,能及早发现问题,默认
check="true"
- 可以通过
check="false"
关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动 - 另外,如果你的Spring容器是懒加载的,或者通过API编程延迟引用服务,请关闭check,否则服务临时不可用时,会抛出异常,拿到null引用,如果
check="false"
,总是会返回引用,当服务恢复时,能自动连上 @DubboReference(check = false)
超时设置
-
由于网络或服务端不可靠,会导致调用出现一种不确定的中间状态(超时)。为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间
-
消费端
- 全局设置
dubbo.consumer.timeout=1000
- 指定方法设置
@DubboReference(timeout = 1000)
- 全局设置
-
服务端
- 全局设置
dubbo.provider.timeout=1000
- 指定方法设置
@DubboService(timeout = 1000)
- 全局设置
重试次数
- retries:重试次数,不包含第一次调用,0代表不重试
@DubboService(retries = 2)
默认次数为2,与timeout相同,可以在不同作用域中设置- 幂等(设置重试次数)【查询、删除、修改】、非幂等(不能设置重试次数)【新增】
- 如果有多个相同的提供者,会使用轮询的方法依次重试
多版本
- 当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用
- 可以按照以下的步骤进行版本迁移
- 在低压力时间段,先升级一半提供者为新版本
- 再将所有消费者升级为新版本
- 然后将剩下的一半提供者升级为新版本
@DubboService(version = "1.0.0")
,参数为*时随机调用一个版本
本地存根
-
远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy
-
通常情况才采用远程服务调用后,对于服务消费者的请求,均是在服务提供者的远程机器上执行的!通过采用本地存根的形式就可以将服务消费者请求的一些验证操作在其本地进行执行,这样就减少了网络的传输,提高了执行效率
-
Stub实现
@DubboService(stub = "true")
public class BarServiceStub implements BarService { private final BarService barService; // 构造函数传入真正的远程代理对象 public BarServiceStub(BarService barService){ this.barService = barService; } public String sayHello(String name) { // 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等 try { return barService.sayHello(name); } catch (Exception e) { // 你可以容错,可以做任何AOP拦截事项 return "容错数据"; } } }
- Stub 必须有可传入 Proxy 的构造函数
- 在 interface 旁边放一个 Stub 实现,它实现
BarService
接口,并有一个传入远程BarService
实例的构造函数
配置覆盖关系
-
对于服务提供者和服务消费者Dubbo均提供了以下四种粒度的设置
-
方法级别
-
接口级别
-
服务消费者级别
-
服务提供者级别
-
-
配置的覆盖规则:
- (精确优先)方法级配置别优于接口级别,接口级别优于全局配置,即小Scope优先
- (消费者优先)精确度相同的情况下,则消费方优先,提供方次之
与SpringBoot整合的三种方式
- 第一种方式:
- 注解的方式:在java类上使用Dubbo提供的注解
- 第二种方式:
- 配置文件方式:在xml配置文件中对Dubbo进行配置,包括服务应用的名称配置,注册中心配置,通信规则,服务提供者信息,服务消费者信息等
- 第三种方式:
- Java Config方法:通过java配置类(在一个java类上使用@Configuration注解)的方式配置注册中心,通信规则,服务提供者信息,服务消费者信息等