服务之间进行调用
RPC
什么是RPC
RPC是Remote Procedure Call的缩写,翻译为:远程过程调用
目标是为了实现两台(多台)计算机\服务器,相互调用方法\通信的解决方案
RPC只是显示远程调用的一套标准,该标准主要规定了两部分内容
-
序列化协议
-
通信协议
通信协议
通信协议指的就是远程调用的通信方式
实际上这个通知的方法可以有多种
例如:写信、闪送
序列化协议
序列化协议指通信内容的格式,双方都要理解这个格式
发送信息是序列化过程,接收信息需要反序列化
Dubbo
上面对RPC有基本认识之后,再学习Dubbo就简单了
Dubbo是一套RPC框架。既然是框架,我们可以再框架结构高度,定义Dubbo中使用的通信协议,使用的序列化协议框架技术,而数据格式由Dubbo定义,我们负责配置之后直接通过客户端调用服务端代码。
可以说Dubbo就是RPC概念的实现
Dubbo是Spring Cloud Alibab提供的框架,能够实现微服务之间相互调用的功能
Dubbo的发展历程
最早是阿里巴巴内部的框架技术
2011年,阿里巴巴把dubbo对外开放
2012年底,阿里巴巴对dubbo停止更新维护
2017年9月,阿里巴巴继续对dubbo提供更新维护
2019年,阿里巴巴将dubbo2.7 以上版本共享给apache(apache.org)
至今,apache dubbo由apache维护更新,阿里巴巴一直更新到2.7
早期的Dubbo是和Spring Cloud平行的,也是微服务
我们学习的Dubbo指的都是2.7之后的版本,也是能够和Spring Cloud Alibaba配置使用的
dubbo对协议的支持
RPC框架分通信协议和序列化协议
Dubbo框架支持多种通信协议和序列化协议,可以通过配置文件进行修改
Dubbo支持的通信协议
dubbo协议(默认),阿里自己写的通信协议
http协议
rmi协议
hessian协议
webservice
…
Dubbo支持的序列化协议
hessian2(默认)
java 序列化
fastjson
kryo
dubbo
…
Dubbo默认情况下,支持的协议有如以下特征:
采用NIO单一长链接
优秀的并发性能,但是处理大型文件的能力差
Dubbo支持高并发和高性能
Dubbo服务的注册与发现
在Dubbo的调用过程中,必须包含注册中心的支持
注册中心推荐使用阿里自己的Nacos,兼容性好,能够发挥最大性能
但是Dubbo也支持其他软件作为注册中心,例如:redis,zookeeper等
服务发现,既消费端自动发现服务地址列表的能力,是微服务框架需要具备的关键额能力,借助于自动化的服务发现,微服务之间可以在无需感知对服务提供者端的部署位置与ip地址的情况下实现通信

consumer 服务的消费者,指服务的调用者
provider 服务的提供者,指的是服务的拥有者
在Dubbo中,远程调用依据是服务的提供者在Nacos中注册的服务名称
一个服务名称,可能有多个运行的实例,任何一个空闲的实例都可以提供服务
常见面试题:Dubbo的注册发现流程
- 首先服务的提供者启动服务时,将自己的具备的服务注册到注册中心,其中包含当前提供者的ip地址和端口号等信息,Dubbo会同时注册该项目提供的远程调用的方法
- 消费者启动项目,也注册到注册中心,同时从注册中心中获得当前项目具备的所有服务列表
- 当注册中心中有新的服务出现时,会通知已经定阅发现的消费者,消费者会更新所有服务列表
- RPC调用,消费者需要调用远程方法时,根据注册中心服务列表的信息,只需服务名称,不需要ip地址和端口号等信息,就可以利用Dubbo调用远程方法了
我们当前csmall项目的远程调用关系,如下图所示

Dubbo实现微服务调用
首先需要确定调用关系

修改csmall-stock模块
当前项目时Dubbo中的服务的提供者
需要将提供给别的服务调用的方法单独编写在一个项目中,这个项目中有的方法,别人才能调用
创建csmall-stock-service项目
删除test、删除resources、删除主启动类
csmall-stock升级为父项目,pom文件添加以下内容:
1 2 3 4
| <packaging>pom</packaging> <modules> <module>csmall-stock-service</module> </modules>
|
csmall-stock-service的pom文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.tedu</groupId> <artifactId>csmall-stock</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath/> </parent> <groupId>cn.tedu</groupId> <artifactId>csmall-stock-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>csmall-stock-service</name> <description>Demo project for Spring Boot</description> <dependencies> <dependency> <groupId>cn.tedu</groupId> <artifactId>csmall-commons</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> </project>
|
把csmall-stock模块中的IStockService接口复制到csmall-stock-service中

到此为止,csmall-stock-service修改结束
创建csmall-stock-webapi项目
父子相认
csmall-stock-webapi的pom文件(把父项目要的依赖剪切过来,还行添加dubbo和csmall-stock-service依赖)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.tedu</groupId> <artifactId>csmall-stock</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath/> </parent> <groupId>cn.tedu</groupId> <artifactId>csmall-stock-webapi</artifactId> <version>0.0.1-SNAPSHOT</version> <name>csmall-stock-webapi</name> <description>Demo project for Spring Boot</description> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>cn.tedu</groupId> <artifactId>csmall-commons</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> </dependency> <dependency> <groupId>cn.tedu</groupId> <artifactId>csmall-stock-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project> ``` csmall-stock的pom文件,最终是 ```XML <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.tedu</groupId> <artifactId>csmall</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath/> </parent> <groupId>cn.tedu</groupId> <artifactId>csmall-stock</artifactId> <version>0.0.1-SNAPSHOT</version> <name>csmall-stock</name> <description>Demo project for Spring Boot</description> <packaging>pom</packaging> <modules> <module>csmall-stock-service</module> <module>csmall-stock-webapi</module> </modules> </project>
|
把csmall-stock中的cn.tedu.csmall.stock和resources目录下的内容复制到csmall-stock-webapi中
csmall-stock中的src目录删除了
接下来是csmall-stock-webapi项目的修改
- 删除service中的接口
- StockServiceImpl中修改StockMapper的包路径
- 修改Knife4jConfiguration中的包路径和分组名称
- 修改MybatisConfiguration中扫描的包路径
关于dubbo的使用
添加dubbo的依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> </dependency> <dependency> <groupId>cn.tedu</groupId> <artifactId>csmall-stock-service</artifactId> <version>0.0.1-SNAPSHOT</version> ``` 在webapi的application-dev.yml中配置dubbo相关的信息 ```yml #配置dubbo信息 dubbo: protocol: #port设置成-1是Dubbo框架支持的特殊写法,会自动动态生成可用的端口,规则是从20880开始, #如果被占用就递增使用 port: -1 #设置连接的名称,一般固定就叫dubbo name: dubbo registry: #声明注册中心的软件类型和ip端口号 address: nacos: //localhost:8848 consumer: #当项目启动时,作为消费者,是否要检查所有远程服务可用,false表示不检查 check: false
|
下面就可以配置实现Dubbo方法提供的步骤了
将业务逻辑层实现类方法声明为Dubbo可调用的方法
在业务逻辑层类上添加@DubboService的注解,该注解描述的类中的所有的放啊都会注册到Nacos,其它服务在订阅时就会发现当前项目提供的业务逻辑层方法,以备Dubbo调用
1 2 3 4 5 6
| @DubboService @Service @Slf4j public class StockServiceImpl implements IStockService { ...... }
|
如果当前项目是服务的提供者,还需要再SpringBoot启动类上添加@EnableDubbo的注解,才让让Dubbo真正生效
如果作为提供者,不添加该注解,其他服务是无法调用该项目的方法
1 2 3 4 5 6 7 8 9
| @SpringBootApplication @EnableDubbo public class CsmallStockWebapiApplication { public static void main(String[] args) { SpringApplication.run(CsmallStockWebapiApplication.class, args); } }
|
先启动nacos,再启动StockWebapi项目,观察结果

修改csmall-cart模块
步骤和stock差不多,参考stock拆分cart
修改csmall-order模块
创建csmall-order-service项目
csmall-order-service和cart以及stock是一样的
创建csmall-order-webapi
需要添加的依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> </dependency> <dependency> <groupId>cn.tedu</groupId> <artifactId>csmall-order-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>cn.tedu</groupId> <artifactId>csmall-stock-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>cn.tedu</groupId> <artifactId>csmall-cart-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
|
yaml文件和cart的一样,代码是正常的从父项目中复制过来
config包下类的修改也是和之前一样
作为dubbo的提供者的配置还是和之前一样
作为dubbo的调用者
service.impl中的修改不一样了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @DubboService @Service @Slf4j public class OrderServiceImpl implements IOrderService { @Autowired private OrderMapper orderMapper; @DubboReference private IStockService stockService; @DubboReference private ICartService dubboCartService; @Override public void orderAdd(OrderAddDTO orderAddDTO) { OrderTbl orderTbl = new OrderTbl(); BeanUtils.copyProperties(orderAddDTO,orderTbl); StockReduceCountDTO stockReduceCountDTO = new StockReduceCountDTO(); stockReduceCountDTO.setCommodityCode(orderAddDTO.getCommodityCode()); stockReduceCountDTO.setReduceCount(orderAddDTO.getCount()); stockService.reduceCommodityCount(stockReduceCountDTO); dubboCartService.deleteUserCart(orderAddDTO.getUserId(),orderAddDTO.getCommodityCode()); orderMapper.insertOrder(orderTbl); log.info("新增订单的信息:{}",orderTbl); } }
|
测试dubbo是否可以使用
首先保证Na操启动
我们项目的启动顺序,尽量保证先启动生产者
再启动消费者
先启动stock、cart,再启动order
(访问路径:http://localhost:20002/doc.html) 运行测试
注意:运行前数据库的数据状态和运行后比较一下
配置business
添加依赖
1 2 3 4 5 6 7 8 9 10 11
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo</artifactId> </dependency> <dependency> <groupId>cn.tedu</groupId> <artifactId>csmall-order-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
|
application-dev.yml和其他项目一样,需要添加dubbo信息
business是单纯的调用者,只需要再service层添加@DubboReference然后调用方法即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Service @Slf4j public class BusinessServiceImpl implements IBusinessService { @DubboReference private IOrderService dubboOrderService; @Override public void buy() { OrderAddDTO orderAddDTO = new OrderAddDTO(); orderAddDTO.setUserId("UU100"); orderAddDTO.setCommodityCode("PC100"); orderAddDTO.setCount(5); orderAddDTO.setMoney(100); dubboOrderService.orderAdd(orderAddDTO); log.info("新增订单的信息为:{}",orderAddDTO); } }
|
测试,观察数据状态
Dubbo生产者消费者配置小结
Dubbo生产者消费者相同的配置
pom文件添加dubbo依赖,yml文件配置dubbo信息
不同配置
- 要有service接口项目
- 提供服务的业务逻辑层实现类要添加@DubboService
- SpringBoot启动类要添加@EnableDubbo注解
- pom文件添加消费者模块的service依赖
- 业务逻辑层远程调用前,模块使用@DubboReference注解获取业务逻辑层实现类对象
配置stock集群的高可用
仍然使用20003、20004、20005端口
按照之前笔记配置即可
为了方便观察运行的哪个实例,我们获取端口输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @DubboService @Service @Slf4j public class StockServiceImpl implements IStockService { @Autowired private StockMapper stockMapper; @Value("${server.port}") private int port; @Override public void reduceCommodityCount(StockReduceCountDTO stockReduceCountDTO) { stockMapper.updateStockByCommodityCode( stockReduceCountDTO.getCommodityCode(), stockReduceCountDTO.getReduceCount()); log.info("库存减少完成!端口是:{}",port); } }
|
我们多次访问business模块进行测试后,可以发现stock模块的三台服务器都可以处理请求,像这样的一个过程是集群中的负载均衡策略
负载均衡
什么是负载均衡
在实际开发中,一个服务基本都是集群模式的,也就是多个功能相同的项目在运行,这样才能承受更高的并发
这时一个请求到这个服务,就需要确定访问哪个服务器

Dubbo框架内部支持负载均衡算法,能够尽可能的让请求在相对空闲的服务器上运行
Dubbo内部默认支持负载均衡算法
在不同的项目中给,可能选用不同的负载均衡策略,以达最好效果
Dubbo内置负载均衡策略
Dubbo内置4种负载均衡算法
-
Random LoadBalance:随机分配策略(默认)
-
Round Robin LoadBalance:权重平滑分配
-
LeastActive LoadBalance:活跃度自动感知分配
-
ConsistantHash LoadBalance:一致性hash算法
实际运行过程中,每个服务器性能不同的
在负载均衡时,会有性能权重,这些策略都考虑权重问题
随机分配策略
假设有三台服务器,经过测试它们的性能权重比为: 5:3:1
下面可以生成一个权重模型

随机生成随机数,在哪个范围内就让哪个服务器运行
优点
算法简单,效率高,长时间运行下,任务分配比例准确
缺点
偶然性高,如果连续的几个随机请求发送到性能弱的服务器,会导致异常甚至宕机
权重平滑策略
如果几个服务器权重一致,那么就是依次运行
但是服务器性能权重一致的可能性很小
所以我们需要权重平滑分配
一个优秀的权重分配算,应该是让每个服务器都有机会运行
如果一个集群性能比为: 5:3:1
1->A 2->A 3->A 4->A 5->A 6->B 7->B 8->B 9->C
…
上面这种安排,连续几个请求到一个服务器中处理,显然是不合理的,我们希望的是所有服务器穿插在一起运行
Dubbo 2.7 之后更新了这个算法,这个算法使用“权重加权算法”优化权重平均分配策略

活跃度自动感知
记录每个服务器处理一次请求的时间(会额外消耗资源)
按着时间比例来分配任务数,运行一次需要时间多的分配的请求数较少
一致性hash算法
根据请求的参数进行hash运算
以后每次相同参数的请求都会访问固定的服务器
因为根据参数选择服务器,不能平均分配到每台服务器,而且如果服务器宕机了,请求也不会被分配到其他的服务器
使用的很少
我们在business中添加了一个在stock数据库中不存在的商品成功了!!但是这样肯定不符合常理,这就是数据的不一致
数据一致相关的知识点我们之间学习过事务