1.dubbo实现微服务调用
1.1确定调用关系
必须按照dubbo规定的配置和行业标准的结构来实现
dubbo调用的好处是直接将要消费的目标(例如order模块中消费stock的方法)编写在当前消费者的业务逻辑层中,无需编写新的代码结构,开发流程不会因为dubbo而变化
1.2修改stock模块
1.2.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>
<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);
}
1.2.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>
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
下面就可以调整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 {
}
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模块的操作
1.3修改cart模块
操作步骤和stock完全一致,参考stock模块即可
1.4修改order模块支持dubbo
既是生产者又是消费者
它消费cart和stock的服务
同时又为business模块提供服务
重构的过程和stock\cart有很多相似,但是也要注意不同
1.4.1创建csmall-order-service项目
这个项目创建的过程和stock\cart模块service项目的步骤和注意事项完全一致
1.4.2创建csmall-order-webapi项目
创建项目后父子相认正常
<!-- dubbo 依赖 -->
<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>
<!-- 作为消费者,order模块需要调用cart和stock模块的业务逻辑层接口 -->
<dependency>
<groupId>cn.tedu</groupId>
<artifactId>csmall-cart-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>
yml文件配置也和cart\stock模块一致
代码也都正常从csmall-order复制到csmall-order-webapi中
删除csmall-order的src目录
在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);
}
}
首先保证Nacos启动
我们项目的启动顺序,尽量保证生产者先启动
启动消费者
stock\cart最后启动order
访问
http://localhost:20002/doc.html运行测试
注意运行前,数据库的数据状态和运行后的比较一下
1.5负载均衡
1.6什么是负载均衡
在实际项目中,一个服务基本都是集群模式的,也就是多个功能相同的项目在运行,这样才能承受更高的并发
dubbo框架内部支持负载均衡算法,能够尽可能的让请求在相对空闲的服务器上运行
Loadbalance:就是负载均衡的意思
1.7dubbo内置负载均衡策略算法
- random loadbalance:随机分配策略(默认)
- round Robin Loadbalance:权重平均分配
- leastactive Loadbalance:活跃度自动感知分配
- consistanthash Loadbalance:一致性hash算法分配
实际运行过程中,每个服务器性能不同
1.7.1随机分配策略
假设我们当前3台服务器,经过测试它们的性能权重比值为5:3:1
在哪个范围内让哪个服务器运行
优点:
算法简单,效率高,长时间运行下,任务分配比例准确
缺点:
偶然性高,如果连续的几个随机请求发送到性能弱的服务器,会导致异常甚至宕机
1.7.2权重平滑分配
如果几个服务器权重一致,那么就是依次运行
但是服务器的性能权重一致的可能性很小
所以我们需要权重平滑分配
一个优秀的权重分配算法,应该是让每个服务器都有机会运行的
1>A 2>A 3>A 4>A 5>A 6>B 7>B 8>B 9>C
10>A
上面的安排中,连续请求一个服务器肯定是不好的,我们希望所有的服务器都能够穿插在一起运行
dubbo2.7之后更新了这个算法使用"平滑加权算法"优化权重平均分配策略
优点:
能够尽可能的在权重要求的情况下,实现请求的穿插运行(交替运行),不会发生随机策略中的偶发情况
缺点
服务器较多时,可能需要减权和复权的计算,需要消耗系统资源
1.7.3活跃度自动感知
记录每个服务器处理一次请求的时间
按照时间比例来分配任务数,运行一次需要时间多的分配的请求数较少
1.7.4一致性Hash算法
根据请求的参数进行hash运算
以后每次相同参数的请求都会访问固定服务器
因为根据参数选择服务器,不能平均分配到每台服务器上
使用的也不多
1.8修改business模块
business模块是我们设计的新增订单业务的触发者,是起点
它是单纯的消费者
我们不需要像生产者一样去创建两个子项目
直接在现有项目上进行修改即可
<!-- 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>
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模块的业务逻辑层实现类中
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运行测试
1.9dubbo生产者消费者配置小结
dubbo生产者消费者相同的配置
1.9.1生产者
1.9.2消费者
2.Seata概述
2.1下载Seata
Releases · seata/seata · GitHub
https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.zip
最好能从项目经理老师处获取
2.2什么是Seata
Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务
也是Spring Cloud Alibaba提供的组件
Seata官方文档
更多信息可以通过官方文档获取
2.3为什么需要Seata
我们之前学习了单体项目中的事务
使用的技术叫Spring声明式事务
能够保证一个业务中所有对数据库的操作要么都成功,要么都失败,来保证数据库的数据完整性
但是在微服务的项目中,业务逻辑层涉及远程调用,当前模块发生异常,无法操作远程服务器回滚
这时要想让远程调用也支持事务功能,就需要使用分布式事务组件Seata
事务的4个特性:ACID特性
- 原子性
- 一致性
- 隔离性
- 永久性
Seata保证微服务远程调用业务的原子性
Seata将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
2.4Seata的运行原理(AT模式)
观察下面事务模型
上面结构是比较典型的远程调用结构
如果account操作数据库失败需要让order模块和storage模块撤销(回滚)操作
声明式事务不能完成这个操作
需要使用Seata来解决
Seata构成部分包含
- 事务协调器TC
- 事务管理器TM
- 资源管理器RM
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通知所有分支将数据库操作回滚,恢复成运行之前的样子
2.5Seata的启动
seata也是java开发的,启动方式和nacos很像
只是启动命令不同
解压后路径不要用中文,不要用空格
也是解压之后的bin目录下
在路径上输入cmd进入dos窗口
D:\tools\seata\seata-server-1.4.2\bin>seata-server.bat -h 127.0.0.1 -m file
输入后,最后出现8091端口的提示即可!