order模块调用stock模块的减少库存的功能
order模块调用cart模块的删除购物车的功能
business模块调用order新增订单的功能
要想实现Dubbo调用
必须按照Dubbo规定的配置和行业标准的结构来实现
Dubbo调用的好处是直接将要消费的目标(例如order模块中消费stock的方法)编写在当前消费者的业务逻辑层中,无需编写新的代码结构,开发流程不会因为Dubbo而变化
因为当前stock模块减少库存数业务是典型的生成者方法,需要被别的模块调用
那么其它模块就必须添加当前库存数减少的业务接口支持
为了减少添加这个给当前项目带来的负担
业界通用做法,是将生产者项目拆分为两个,
其中一个项目只有业务逻辑层接口
另一个项目包含正常的配置和所有业务代码
当消费者需要时只需要添加包含业务逻辑层接口的项目的依赖即可
创建csmall-stock-service项目
删除test\删除resources\删除SpringBoot启动类
csmall-stock升格为父项目,所以也要修改它的pom文件
- <description>Demo project for Spring Bootdescription>
- <packaging>pompackaging>
- <modules>
- <module>csmall-stock-servicemodule>
- modules>
csmall-stock-service项目的pom文件添加最低要求的依赖
- "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.0modelVersion>
- <parent>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-stockartifactId>
- <version>0.0.1-SNAPSHOTversion>
- <relativePath/>
- parent>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-stock-serviceartifactId>
- <version>0.0.1-SNAPSHOTversion>
- <name>csmall-stock-servicename>
- <description>Demo project for Spring Bootdescription>
- <dependencies>
- <dependency>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-commonsartifactId>
- <version>0.0.1-SNAPSHOTversion>
- dependency>
- dependencies>
-
- project>
然后将原有的业务逻辑层接口IStockService复制到这个项目中即可
- public interface IStockService {
- // 根据商品编号减少库存的方法
- void reduceCommodityCount(StockReduceCountDTO stockReduceCountDTO);
- }
webapi项目包含stock项目原有的所有配置和业务代码
创建好项目之后删除test文件夹、删除application.properties文件
然后父子相认
最终csmall-stock项目的pom文件为
- "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.0modelVersion>
- <parent>
- <groupId>cn.tedugroupId>
- <artifactId>csmallartifactId>
- <version>0.0.1-SNAPSHOTversion>
- <relativePath/>
- parent>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-stockartifactId>
- <version>0.0.1-SNAPSHOTversion>
- <name>csmall-stockname>
- <description>Demo project for Spring Bootdescription>
- <packaging>pompackaging>
- <modules>
- <module>csmall-stock-servicemodule>
- <module>csmall-stock-webapimodule>
- modules>
- project>
csmall-stock-webapi项目的pom文件为
- "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.0modelVersion>
- <parent>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-stockartifactId>
- <version>0.0.1-SNAPSHOTversion>
- <relativePath/>
- parent>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-stock-webapiartifactId>
- <version>0.0.1-SNAPSHOTversion>
- <name>csmall-stock-webapiname>
- <description>Demo project for Spring Bootdescription>
- <dependencies>
-
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webartifactId>
- dependency>
-
- <dependency>
- <groupId>org.mybatis.spring.bootgroupId>
- <artifactId>mybatis-spring-boot-starterartifactId>
- dependency>
-
- <dependency>
- <groupId>com.alibabagroupId>
- <artifactId>druidartifactId>
- dependency>
-
- <dependency>
- <groupId>mysqlgroupId>
- <artifactId>mysql-connector-javaartifactId>
- dependency>
-
- <dependency>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-commonsartifactId>
- <version>0.0.1-SNAPSHOTversion>
- dependency>
-
- <dependency>
- <groupId>com.github.xiaoymingroupId>
- <artifactId>knife4j-spring-boot-starterartifactId>
- dependency>
-
- <dependency>
- <groupId>com.alibaba.cloudgroupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
- dependency>
-
- <dependency>
- <groupId>com.alibaba.cloudgroupId>
- <artifactId>spring-cloud-starter-dubboartifactId>
- dependency>
-
- <dependency>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-stock-serviceartifactId>
- <version>0.0.1-SNAPSHOTversion>
- dependency>
- dependencies>
-
- project>
1.2.3上面的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开始寻找可用的端口号,如果被占用,就递增寻找下一个,直到找到可用为止
- 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文件夹了
下面就可以调整webapi项目的中配置路径了
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方法提供的步骤了
1.2.4将业务逻辑层实现类方法声明为Dubbo可调用的方法
当前stock模块是单纯的生产者
- // @DubboService注解标记的业务逻辑层实现类,其中的所有方法会注册到Nacos
- // 其它服务在"订阅"时,就会"发现"当前项目提供的服务(业务逻辑层方法),以便后续在需要时调用
- @DubboService
- @Service
- @Slf4j
- public class StockServiceImpl implements IStockService {
- //内容略....
- }
如果当前项目是服务的提供者(生产者)
还需要在SpringBoot启动类上添加@EnableDubbo的注解,才能真正让Dubbo功能生效
- @SpringBootApplication
- // 如果当前项目是Dubbo的生产者,必须在当前项目的SpringBoot启动类上添加下面注解
- // 才能正常正确的将当前项目提供的服务注册到Nacos
- @EnableDubbo
- public class CsmallStockWebapiApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(CsmallStockWebapiApplication.class, args);
- }
-
- }
先启动nacos在启动
再启动stockWebapi项目
作业:
按上面stock模块的操作
改写cart模块的结构,以实现Dubbo生产者功能
操作步骤和stock完全一致,参考stock模块即可
因为order模块在Dubbo的调用关系中
既是生产者又是消费者
它消费cart和stock的服务
同时又为business模块提供服务
重构的过程和stock\cart有很多相似,但是也要注意不同
这个项目创建的过程和stock\cart模块service项目的步骤和注意事项完全一致
创建项目后父子相认正常
子项目的pom文件依赖需要添加下面内容
- <dependency>
- <groupId>com.alibaba.cloudgroupId>
- <artifactId>spring-cloud-starter-dubboartifactId>
- dependency>
- <dependency>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-order-serviceartifactId>
- <version>0.0.1-SNAPSHOTversion>
- dependency>
- <dependency>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-cart-serviceartifactId>
- <version>0.0.1-SNAPSHOTversion>
- dependency>
- <dependency>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-stock-serviceartifactId>
- <version>0.0.1-SNAPSHOTversion>
- dependency>
yml文件配置也和cart\stock模块一致
代码也都正常从csmall-order复制到csmall-order-webapi中
删除csmall-order的src目录
config包中的配置,修改为正确包名
在OrderServiceImpl业务逻辑层实现类中
添加生产者的注解,同时利用Dubbo消费stock和cart模块的方法
Reference:引用
- // Order模块既是生产者也是消费者,所以作为生产者,还是要编写@DubboService
- @DubboService
- @Service
- @Slf4j
- public class OrderServiceImpl implements IOrderService {
-
- @Autowired
- private OrderMapper orderMapper;
- // 添加@DubboReference注解,表示当前业务逻辑层代码,要消费其它模块的服务
- // 可以编写当前Nacos中注册的其它模块的业务逻辑层接口
- // 因为在Nacos中注册的是接口的实现类,可以实现自动装配实现类的效果
- // 先添加stock模块的业务对象,有些公司要求dubbo引用的对象使用dubbo开头
- @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中的信息新增到订单表中的功能
- // 实例化一个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框架内部支持负载均衡算法,能够尽可能的让请求在相对空闲的服务器上运行
在不同的项目中,可能选用不同的负载均衡策略,以达到最好效果
Loadbalance:就是负载均衡的意思
Dubbo内置4种负载均衡算法
实际运行过程中,每个服务器性能不同
在负载均衡时,都会有性能权重,这些策略算法都考虑权重问题
假设我们当前3台服务器,经过测试它们的性能权重比值为5:3:1
下面可以生成一个权重模型
随机生成随机数
在哪个范围内让哪个服务器运行
优点:
算法简单,效率高,长时间运行下,任务分配比例准确
缺点:
偶然性高,如果连续的几个随机请求发送到性能弱的服务器,会导致异常甚至宕机
如果几个服务器权重一致,那么就是依次运行
但是服务器的性能权重一致的可能性很小
所以我们需要权重平滑分配
一个优秀的权重分配算法,应该是让每个服务器都有机会运行的
如果一个集群服务器性能比为5:3:1
1>A 2>A 3>A 4>A 5>A 6>B 7>B 8>B 9>C
10>A
上面的安排中,连续请求一个服务器肯定是不好的,我们希望所有的服务器都能够穿插在一起运行
Dubbo2.7之后更新了这个算法使用"平滑加权算法"优化权重平均分配策略
优点:
能够尽可能的在权重要求的情况下,实现请求的穿插运行(交替运行),不会发生随机策略中的偶发情况
缺点
服务器较多时,可能需要减权和复权的计算,需要消耗系统资源
记录每个服务器处理一次请求的时间
按照时间比例来分配任务数,运行一次需要时间多的分配的请求数较少
根据请求的参数进行hash运算
以后每次相同参数的请求都会访问固定服务器
因为根据参数选择服务器,不能平均分配到每台服务器上
使用的也不多
business模块是我们设计的新增订单业务的触发者,是起点
它是单纯的消费者
我们不需要像生产者一样去创建两个子项目
直接在现有项目上进行修改即可
pom文件直接添加dubbo和order业务逻辑接口的依赖
- <dependency>
- <groupId>com.alibaba.cloudgroupId>
- <artifactId>spring-cloud-starter-dubboartifactId>
- dependency>
- <dependency>
- <groupId>cn.tedugroupId>
- <artifactId>csmall-order-serviceartifactId>
- <version>0.0.1-SNAPSHOTversion>
- dependency>
application-dev.yml添加Dubbo的配置
- spring:
- application:
- # 为当前项目起名,这个名字会被Nacos记录并使用
- name: nacos-business
- cloud:
- nacos:
- discovery:
- # 配置Nacos所在的位置,用于注册时提交信息
- server-addr: localhost:8848
- 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模块的新增订单的方法
- // 单纯的消费者,不需要在类上添加@DubboService
- // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
- @DubboReference
- private IOrderService dubboOrderService;
-
- @Override
- public void buy() {
- // 模拟购买业务
- // 创建用于新增订单的DTO实体OrderAddDTO
- OrderAddDTO orderAddDTO=new OrderAddDTO();
- // 为orderAddDTO赋值
- orderAddDTO.setUserId("UU100");
- orderAddDTO.setCommodityCode("PC100");
- orderAddDTO.setCount(10);
- orderAddDTO.setMoney(666);
- // 因为是模拟购买,现在还不能调用order模块,所以只是输出
- log.info("新增订单的信息为:{}",orderAddDTO);
- // dubbo调用业务
- // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
- dubboOrderService.orderAdd(orderAddDTO);
- }
- }
Springboot启动类不必要写@EnableDubbo因为business是单纯的消费者
启动business项目(前提是cart\stock\order正在运行)
http://localhost:20000/doc.html运行测试
Dubbo生产者消费者相同的配置
pom文件添加dubbo依赖,yml文件配置dubbo信息
1.9.1生产者
要有service接口项目
提供服务的业务逻辑层实现类要添加@DubboService注解
SpringBoot启动类要添加@EnableDubbo注解
1.9.2消费者
Releases · seata/seata · GitHub
https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.zip
最好能从项目经理老师处获取
Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务
也是Spring Cloud Alibaba提供的组件
Seata官方文档
更多信息可以通过官方文档获取
我们之前学习了单体项目中的事务
使用的技术叫Spring声明式事务
能够保证一个业务中所有对数据库的操作要么都成功,要么都失败,来保证数据库的数据完整性
但是在微服务的项目中,业务逻辑层涉及远程调用,当前模块发生异常,无法操作远程服务器回滚
这时要想让远程调用也支持事务功能,就需要使用分布式事务组件Seata
事务的4个特性:ACID特性
Seata保证微服务远程调用业务的原子性
Seata将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
观察下面事务模型
上面结构是比较典型的远程调用结构
如果account操作数据库失败需要让order模块和storage模块撤销(回滚)操作
声明式事务不能完成这个操作
需要使用Seata来解决
Seata构成部分包含
我们项目使用AT(自动)模式完成分布式事务的解决
AT模式运行过程
1.事务的发起方(TM)会向事务协调器(TC)申请一个全局事务id,并保存
2.Seata会管理事务中所有相关的参与方的数据源,将数据操作之前和之后的镜像都保存在undo_log表中,这个表是seata组件规定的表,没有它就不能实现效果,依靠它来实现提交(commit)或回滚(roll back)的操作
3.事务的发起方(TM)会连同全局id一起通过远程调用运行资源管理器(RM)中的方法
4.RM接收到全局id,去运行指定方法,并将运行结果的状态发送给TC
5.如果所有分支运行都正常,事务管理器(TM)会通过事务协调器通知所有模块执行数据库操作,真正影响数据库内容,反之如果有任何一个分支模块运行异常,都会通知TC,再由TC通知所有分支将数据库操作回滚,恢复成运行之前的样子
seata也是java开发的,启动方式和nacos很像
只是启动命令不同
它要求配置环境变量中Path属性值有java的bin目录路径
解压后路径不要用中文,不要用空格
也是解压之后的bin目录下
在路径上输入cmd进入dos窗口
D:\tools\seata\seata-server-1.4.2\bin>seata-server.bat -h 127.0.0.1 -m file
输入后,最后出现8091端口的提示即可!