服务之间进行调用
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数据库中不存在的商品成功了!!但是这样肯定不符合常理,这就是数据的不一致
数据一致相关的知识点我们之间学习过事务