一.确定调用结构
下图为典型购物平台订单模块,购物车模块,库存模块结构图
以上图中各模块关系为例
- order模块调用stock模块的减少库存的功能
- order模块调用cart模块的删除购物车的功能
- business模块调用order新增订单的功能
要想实现Dubbo调用
必须按照Dubbo规定的配置和行业标准的结构来实现
Dubbo调用的好处是直接将要消费的目标(例如order模块中消费stock的方法)编写在当前消费者的业务逻辑层中,无需编写新的代码结构,开发流程不会因为Dubbo而变化
二.修改stock模块
1.创建csmall-stock-service项目
因为当前stock模块减少库存数业务是典型的生成者方法,需要被别的模块调用
那么其它模块就必须添加当前库存数减少的业务接口支持
为了减少添加这个给当前项目带来的负担
业界通用做法,是将生产者项目拆分为两个,
其中一个项目只有业务逻辑层接口
另一个项目包含正常的配置和所有业务代码
当消费者需要时只需要添加包含业务逻辑层接口的项目的依赖即可
创建csmall-stock-service项目
删除test\删除resources\删除SpringBoot启动类
csmall-stock升格为父项目,所以也要修改它的pom文件
<description>Demo project for Spring Boot</description>
<packaging>pom</packaging>
<modules>
<module>csmall-stock-service</module>
</modules>
csmall-stock-service项目的pom文件添加最低要求的依赖
<?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/> <!-- lookup parent from repository -->
</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>
<!-- 当前业务逻辑层接口项目,只需要commons模块的依赖即可 -->
<dependencies>
<dependency>
<groupId>cn.tedu</groupId>
<artifactId>csmall-commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
然后将原有的业务逻辑层接口IStockService复制到这个项目中即可
public interface IStockService {
// 减少库存的业务逻辑层方法
void reduceCommodityCount(StockReduceCountDTO stockReduceCountDTO);
}
2.创建csmall-stock-webapi项目
webapi项目包含stock项目原有的所有配置和业务代码
创建好项目之后删除test文件夹、删除application.properties文件
然后父子相认
最终csmall-stock项目的pom文件为
<?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/> <!-- lookup parent from repository -->
</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-webapi项目的pom文件为
<?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/> <!-- lookup parent from repository -->
</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>
<!--web实例-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis整合springboot-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--alibaba 数据源德鲁伊-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--all-common依赖-->
<dependency>
<groupId>cn.tedu</groupId>
<artifactId>csmall-commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--在线api文档-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
<!-- Nacos注册依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Dubbo的依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!-- 当前项目为了能够在业务逻辑层中正常实现业务逻辑层接口
需要添加业务逻辑层接口所在的csmall-stock-service项目的依赖 -->
<dependency>
<groupId>cn.tedu</groupId>
<artifactId>csmall-stock-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
上面的pom文件不只是将原有的stock的pom文件依赖复制,而且添加了Dubbo和业务逻辑层接口的依赖
将csmall-stock项目的application.yml和application-dev.yml复制到csmall-stock-webapi项目的resources文件夹下
yml文件其它的不动,但是在dev.yml文件中要添加dubbo的配置信息
spring:
datasource:
url: jdbc:mysql://localhost:3306/csmall_db?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root
password: root
application:
# 为当前项目起名,这个名字会被Nacos记录并使用
name: nacos-stock
cloud:
nacos:
discovery:
# 配置Nacos所在的位置,用于注册时提交信息
server-addr: localhost:8848
dubbo:
protocol:
# port设置-1 表示当前dubbo端口号支持自动生成
# 生成规则是从20880开始寻找可用的端口号,如果当前端口号被占用,就会自动加1,直到找可用的为止
port: -1
# 设置连接的名称,一般固定为dubbo即可
name: dubbo
registry:
# 声明当前dubbo的注册中心类型和位置
address: nacos://localhost:8848
consumer:
# 当本项目启动时,是否检查当前项目需要的所有Dubbo服务是否可用
# 我们设置它为false,表示启动时不检查,能够减少出错的情况
check: false
开始复制代码
我们先将csmall-stock模块中的IStockService接口删除
然后直接复制config\mapper\controller\service.impl四个包
粘贴到webapi项目中
业务逻辑层实现类需要重新导入Mapper的包来实现正确编译
注意impl的包名也要修改->service.impl
然后就可以删除原stock模块的src文件夹了
Knife4jConfiguration:
/**
* 【重要】指定Controller包路径
*/
private String basePackage = "cn.tedu.csmall.stock.webapi.controller";
MyBatisConfiguration
@Configuration
// MyBatis框架扫描mapper接口包的注解
@MapperScan("cn.tedu.csmall.stock.webapi.mapper")
public class MyBatisConfiguration {
}
下面就可以配置实现Dubbo方法提供的步骤了
3.将业务逻辑层实现类方法声明为Dubbo可调用的方法
当前stock模块是单纯的生产者
// @DubboService注解标记的业务逻辑层实现类,其中的所有方法都会注册到Nacos
// 在其他服务启动"订阅"时,就会"发现"当前类中的所有服务(业务逻辑层方法),并表示允许调用
@DubboService
@Service
@Slf4j
public class StockServiceImpl implements IStockService {
//内容略....
}
如果当前项目是服务的提供者(生产者)
还需要在SpringBoot启动类上添加@EnableDubbo的注解,才能真正让Dubbo功能生效
@SpringBootApplication
// 如果当前项目是Dubbo的生产者,必须在当前项目的SpringBoot启动类上添加@EnableDubbo这个注解
// 添加注解后,当前项目的所有服务才能正确发布到Nacos中!
@EnableDubbo
public class CsmallStockWebapiApplication {
public static void main(String[] args) {
SpringApplication.run(CsmallStockWebapiApplication.class, args);
}
}
先启动nacos在启动
再启动stockWebapi项目
三.修改cart模块
操作步骤和stock完全一致,参考stock模块即可
四.修改order模块支持Dubbo
因为order模块在Dubbo的调用关系中
既是生产者又是消费者
它消费cart和stock的服务
同时又为business模块提供服务
重构的过程和stock\cart有很多相似,但是也要注意不同
1.创建csmall-order-service项目
这个项目创建的过程和stock\cart模块service项目的步骤和注意事项完全一致
2.创建csmall-order-webapi项目
创建项目后父子相认正常
子项目的pom文件依赖需要额外添加下面内容
<!-- Dubbo的依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!-- 当前项目为了能够在业务逻辑层中正常实现业务逻辑层接口
需要添加业务逻辑层接口所在的csmall-order-service项目的依赖 -->
<dependency>
<groupId>cn.tedu</groupId>
<artifactId>csmall-order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 作为消费者,order模块需要调用stock和cart模块的业务逻辑层,
所以要添加它们的依赖 -->
<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>
yml文件配置也和cart\stock模块一致
代码也都正常从csmall-order复制到csmall-order-webapi中
删除csmall-order的src目录
config包中的配置,修改为正确包名
在OrderServiceImpl业务逻辑层实现类中
添加生产者的注解,同时利用Dubbo消费stock和cart模块的方法
Reference:引用
// order模块的功能会被business模块调用,所以它仍然是生产者,@DubboService要写
@DubboService
@Service
@Slf4j
public class OrderServiceImpl implements IOrderService {
@Autowired
private OrderMapper orderMapper;
// 添加@DubboReference注解,表示当前业务逻辑层要消费其他模块的服务
// 可以编写当前Nacos中注册的其他业务模块的逻辑层接口
// 因为Nacos中注册了业务的实现类,所以声明的接口会自动匹配到实现类对象
// 先添加stock模块的业务逻辑层接口,在添加cart模块
@DubboReference
private IStockService dubboStockService;
@DubboReference
private ICartService dubboCartService;
@Override
public void orderAdd(OrderAddDTO orderAddDTO) {
// 1.减少订单中商品的库存数(要调用stock模块的功能)
// 实例化减少库存业务的DTO对象
StockReduceCountDTO countDTO=new StockReduceCountDTO();
countDTO.setCommodityCode(orderAddDTO.getCommodityCode());
countDTO.setReduceCount(orderAddDTO.getCount());
// 执行dubbo调用完成stock模块减少库存的方法
dubboStockService.reduceCommodityCount(countDTO);
// 2.从购物车中删除订单中选择中的商品(要调用cart模块的功能)
dubboCartService.deleteUserCart(orderAddDTO.getUserId(),
orderAddDTO.getCommodityCode());
// 3.将orderAddDTO中的信息新增到数据库订单表中
// 要将orderAddDTO对象中的属性赋值给Order类型对象的同名属性
Order order=new Order();
BeanUtils.copyProperties(orderAddDTO,order);
// 执行新增
orderMapper.insertOrder(order);
log.info("新增订单信息为:{}",order);
}
}
因为order模块也是生产者@EnableDubbo注解仍然要写
@SpringBootApplication
@EnableDubbo
public class CsmallOrderWebapiApplication {
public static void main(String[] args) {
SpringApplication.run(CsmallOrderWebapiApplication.class, args);
}
}
我们可以测试这个Dubbo的功能
首先保证Nacos启动
我们项目的启动顺序,尽量保证生产者先启动
启动消费者
stock\cart最后启动order
访问
http://localhost:20002/doc.html运行测试
注意运行前,数据库的数据状态和运行后的比较一下
我们可以再复制一个项目出来
更改端口号后启动它,这样同一个服务就可能出现多个实例
当Dubbo调用这个实例时,就需要一个调用策略,简单来说,就是要可能调用到其中任何一个服务器
而这个策略有一个名称叫负载均衡(微服务07-负载均衡_teyruthy的博客-CSDN博客)
五.修改business模块
business模块是我们设计的新增订单业务的触发者,是起点
它是单纯的消费者
我们不需要像生产者一样去创建两个子项目
直接在现有项目上进行修改即可
pom文件直接添加dubbo和order业务逻辑接口的依赖
<!-- dubbo依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!-- 添加order模块的业务逻辑层接口的依赖 -->
<dependency>
<groupId>cn.tedu</groupId>
<artifactId>csmall-order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
application-dev.yml添加Dubbo的配置
spring:
application:
# 为当前项目起名,这个名字会被Nacos收集,并显示在注册列表中
name: nacos-business
cloud:
nacos:
discovery:
# 配置Nacos的位置,用于当前项目连接
server-addr: localhost:8848
# ephemeral设置当前项目启动时注册到nacos的类型 true(默认):临时实例 false:永久实例
ephemeral: false
dubbo:
protocol:
port: -1
name: dubbo
registry:
address: nacos://localhost:8848
consumer:
check: false
我们要在当前busindess模块的业务逻辑层实现类中
实现Dubbo调用order模块的生成订单方法
BusinessServiceImpl类中
@Service
@Slf4j
public class BusinessServiceImpl implements IBusinessService {
// Dubbo调用Order模块的新增订单功能
// Business是单纯的消费者,不需要在类上编写@DubboService
@DubboReference
private IOrderService dubboOrderService;
@Override
public void buy() {
// 实例化一个用于新增订单的DTO类对象
// 并为它赋值
OrderAddDTO orderAddDTO=new OrderAddDTO();
orderAddDTO.setUserId("UU100");
orderAddDTO.setCommodityCode("PC100");
orderAddDTO.setCount(3);
orderAddDTO.setMoney(300);
log.info("新增订单信息为:{}",orderAddDTO);
// dubbo调用
dubboOrderService.orderAdd(orderAddDTO);
}
}
Springboot启动类不必要写@EnableDubbo因为business是单纯的消费者
启动business项目(前提是cart\stock\order正在运行)
http://localhost:20000/doc.html运行测试
六.Dubbo生产者消费者配置小结
Dubbo生产者消费者相同的配置
pom文件添加dubbo依赖,yml文件配置dubbo信息
生产者
-
要有service接口项目
-
提供服务的业务逻辑层实现类要添加@DubboService注解
-
SpringBoot启动类要添加@EnableDubbo注解
消费者
- pom文件添加消费模块的service依赖
- 业务逻辑层远程调用前,模块使用@DubboReference注解获取业务逻辑层实现类对象
七.Dubbo调用常见错误
No provider available from registry localhost:8848 for service cn.tedu.csmall.stock.service.IStockService on consumer 192.168.126.1 use dubbo version 2.7.8, please check status of providers(disabled, not registered or in blacklist).
(disabled, not registered or in blacklist)是这个错误信息的特征
发生这个错误原因是消费者无法在指定的位置找到需要的服务
- 检查调用目标的服务是否启动(上面示例中可能是因为stock模块没有启动导致的)
- 检查被调用的目标服务SpringBoot启动类是否编写的@EnableDubbo注解
- 检查被调用的模块的业务逻辑层实现类是否编写了@DubboService注