SpringCloud是由Spring提供的一套能够快速搭建微服务架构程序的框架集
SpringCloud本身不是一个框架,而是一系列框架的统称
SpringClound就是为了搭建微服务架构才出现的
有人将SpringCloud称之为"Spring全家桶",广义上代指所有Spring的产品
Spring自己编写的框架和软件
Netflix(奈非):早期提供了很多微服务组件
alibaba(阿里巴巴):新版本SpringCloud推荐使用(使用频率迅速提升)
微服务的注册中心
微服务间的调用
微服务的分布式事务
微服务的限流
微服务的网关
.............
Nacos是Spring Cloud Alibaba提供的一个软件
这个软件具有微服务注册中心功能
也就是当前微服务架构中的所有微服务项目都需要到Nacos注册才能成为这个微服务项目的一部分
Nacos是一个能够接收所有微服务项目信息的组件
只有将信息"注册"到Nacos,这个项目才会成为微服务架构中的一个组成部分
安装和启动Nacos
前提
需要当前计算机安装并配置java环境变量
因为Nacos是java开发的,所以要运行必须配置java环境变量
Nacos 下载路径:
https://github.com/alibaba/nacos/releases/download/1.4.3/nacos-server-1.4.3.zip
面试题
Nacos系统内置周期性检查和报告工作,称之为"心跳"
客户端或服务器会周期的检查状态,运行固定代码,保证能够检测模块
Nocos实例的心跳又分为类两种
临时实例心跳
永久实例心跳
在后面配置中,如果采用默认配置,当前实例就是永久实例
spring:
application:
name: xxxx
cloud:
nacos:
discovery:
ephemeral: false # 默认false实例为永久实例。true:临时; false:永久
server-addr: localhost:8848
临时实例基于心跳方式做健康检测
永久实例是Nacos主动探测健康状态
客户端(各个模块)每隔五秒自动向Nacos发送心跳包
包中包含微服务名称,ip地址 port(端口号),集群名称信息
Nacos接收到信息后,判断当前Nacos中是否包含这个包中的信息
****如果不包,证明这是一个新的实例,进行注册操作
****如果存在,记录本次心跳时间,设置为健康状态
整体流程有个别名:"心跳续约"
永久实例是Nacos周期性向客户端进行检测
Nacos每隔15秒向客户端发送一个检测请求,如果第一个15 秒客户端没有给出响应,就会被标记为"肺健康状态"
如果两个连续的15秒(一共30秒)都没有响应,就会从注册列表中剔除这个服务
整体流程有个别名"心跳检测"
将压缩包解压(注意不要有中文路径或空格)
打开解压得到的文件夹后打卡bin目录会有如下内容
startup.cmd是windows系统启动Nacos的命令
shutdown.cmd是windows系统停止Nacos的命令
.sh结尾的文件是linux和mac系统的启动和停止文件
启动Nacos不能直接双击startup.cmd
而需要打开dos窗口来执行
Win+R输入cmd
如果不指定会默认以集群模式运行,无法完成功能
如果运行成功会显示8848端口
打开浏览器输入地址
http://localhost:8848/nacos
如果首次访问没有响应,可以尝试从新解压和运行,再访问
登录系统
用户名和密码都是nacos
进入后会看到列表后台
注意不要关闭doc窗口,一旦关闭,nacos就停止了
要注册到Nacos的项目添加如下配置即可
添加pom.xml文件依赖
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
application-dev.yml文件添加配置
spring:
application:
name: nacos-business # 定义当前服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 # 指定正在运行的Nacos服务器的位置
上面配置中nacos-business是当前服务名称,每个不同功能模块名称要不同
nacos ip地址和端口号根据实际情况编写
我们启动business,在nacos服务列表中观察
出现信息表示一切正常
可以复制项目然后修改端口号,启动同名服务的项目,这样服务列表中就出现多个实例
实际开发中,一般情况都会有多个实例去分但同一个服务
每次开机都需要启动Nacos
项目的启动中就会出现Nacos的启动项了
了解一下RPC的概念
RPC是Remote Procedure Call 翻译为: 远程过程调用
它是为了实现两个不再一起的服务器\计算机,实现相互调用方法的解决方案
RPC主要包含了2个部分的内容
序列化协议
通信协议
理解PRC的概念图
如果远程调用如下图
通信协议:
当老婆在外面时,需要借助通讯工具通知老公来完成什么操作
在这个流程中通信协议就指老婆使用什么方式通知老公要洗碗
可以是手机,也可以写信,可以飞鸽传书
序列化协议指传输信息的内容是什么格式,双方都要能够理解这个格式
例如老婆说中文,老公要理解中文,其他语言也一样
发送信息是序列化的过程,接收信息是反序列化的过程
这样他们才能明确调用的目的
这样的流程就是我们生活中的RPC使用的场景
Dubbo是一套RPC框架,既然是框架,我们可以在框架结构高度,定义Dubbo中使用通信协议,使用的序列化框架技术,而数据格式由Dubbo定义,我们负责配置之后直接通过客户端调用服务端代码
Dubbo中默认的通信协议是Dubbo自己写的协议
序列化协议使用的就是我们之前用过的json
但是我们也要知道,通信协议和序列化协议是可以通过配置文件修改的
使用的生活中老婆和老公的例子 在程序是什么样的模型呢
发起调用的一方从当前项目的业务层,调用被调用一方的业务层方法
也可能是发起调用的一方从控制层,调用被调用一方的业务层方法
总的来说,被调用的方法一定是业务逻辑层方法
Dubbo框架的优点之一就是没有破坏controller>service>mapper的运行流程
因为Dubbo内置了序列化协议和通信协议,所有会有下面的特征
采用NIO单一长链接
优秀的高并发处理性能
编写简单 提升开发效率
使用Dubbo实现远程调用必须有Nacos的支持
服务发现,即消费端自动发现服务地址列表的能力,是微服务框架需要具备的关键能力,借助于自动化的服务发现,微服务之间可以在无需感知对端部署位与IP地址的情况下实现通信
服务的消费者就是服务的调用者(老婆)
服务的生产者就是服务的提供者(老公)
Dubbo调用远程服务,无需要指定IP地址端口号,只需要知道它的服务名称即可
一个服务名称可能有多个运行的实例,任何一个空闲都可以提供服务
1. 首先服务的提供者启动服务到Nacos注册中心进行注册,包括各种ip端口信息,Dubbo会同时注册该项目提供远程调用的方法
2.服务的消费者(使用者)注册到注册中心 即 订阅发现
3.当有新的远程调用方法注册到注册中心时,注册中心会通知服务的消费者有哪些新的方法,如何调用的信息
4.RPC调用,在上面条件满足的情况下,服务的调用者无需知道ip和端口号,只需要服务名称机也可以调用到服务提供者的方法
了解调用流程
添加Dubbo的依赖和支持
stock模块中要创建一个新的项目csmall-stock-service
删除test\删除resources\删除SpringBoot启动类
这个项目就是一个保存业务逻辑层接口的项目
pom文件也要父子相
4.0.0
cn.tedu
csmall-stock
0.0.1-SNAPSHOT
cn.tedu
csmall-stock-service
0.0.1-SNAPSHOT
csmall-stock-service
Demo project for Spring Boot
cn.tedu
csmall-commons
0.0.1-SNAPSHOT
复制IStockService到当前项目
public interface IStockService {
void reduceCommodityCount(StockReduceCountDTO stockReduceCountDTO);
}
在stock模块中在创建一个字项目csmall-stock-webapi
删除test文件夹
然后复制原stock的依赖到webapi然添加dubbo的依赖
pom.xml文件如下
4.0.0
cn.tedu
csmall-stock
0.0.1-SNAPSHOT
cn.tedu
csmall-stock-webapi
0.0.1-SNAPSHOT
csmall-stock-webapi
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
com.alibaba
druid
mysql
mysql-connector-java
cn.tedu
csmall-commons
0.0.1-SNAPSHOT
com.github.xiaoymin
knife4j-spring-boot-starter
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-dubbo
cn.tedu
csmall-stock-service
0.0.1-SNAPSHOT
csmall-stock二级父项目pom文件也要修改为父项目的格式
4.0.0
cn.tedu
csmall
0.0.1-SNAPSHOT
cn.tedu
csmall-stock
0.0.1-SNAPSHOT
csmall-stock
Demo project for Spring Boot
pom
csmall-stock-service
csmall-stock-webapi
将原有csmall-stock的application.yml和application-dev.yml复制到csmall-stock-webapi项目的resources文件夹下
在application-dev.yml添加dubbo的依赖
spring:
application:
name: nacos-stock # 定义当前服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 # 指定正在运行的Nacos服务器的位置
datasource:
url: jdbc:mysql://localhost:3306/csmall_db?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root
password: root
dubbo:
application:
name: nacos-stock # 一般情况下会和spring.application.name设置一样的值,默认也是这个值,可以省略
protocol:
port: -1 # 设置dubbo服务调用的端口 设置-1表示自动生成,生成规则是从20880开始递增
name: dubbo # 端口名称固定dubbo即可
registry:
address: nacos://localhost:8848 # 表示当前Dubbo的注册中心类型是Nacos,地址是后面的内容
consumer:
check: false # 设置false表示服务启动时,不检查标定的可调用的远程服务是否存在,避免报错
将csmall-stock中所有java类(除了业务逻辑层接口)都复制到webapi项目中完成一些必要要的包配置的变化
Knife4jConfiguration:
/**
* 【重要】指定Controller包路径
*/
private String basePackage = "cn.tedu.csmall.stock.webapi.controller";
MyBatisConfiguration
@Configuration
@MapperScan("cn.tedu.csmall.stock.webapi.mapper")
public class MyBatisConfiguration {
}
StockServiceImpl
// ↓↓↓↓↓↓↓↓
@DubboService
@Service
@Slf4j
public class StockServiceImpl implements IStockService {
@Autowired
private StockMapper stockMapper;
@Override
public void reduceCommodityCount(StockReduceCountDTO stockReduceCountDTO) {
log.info("控制层调用减少商品库存--开始");
stockMapper.updateStockCountByCommodityCoud(stockReduceCountDTO.getCommodityCode(),stockReduceCountDTO.getReduceCount());
log.info("商品减库存入库--结束");
}
}
CsmllStockWebapiApplication启动类
@SpringBootApplication
// ↓↓↓↓↓↓↓↓
@EnableDubbo
public class CsmallStockWebapiApplication {
public static void main(String[] args) {
SpringApplication.run(CsmallStockWebapiApplication.class, args);
}
}
删除stock模块的src
操作步骤和stock模块一致
只是csmall-order-webapi项目pom文件需要添加stock和cart的业务逻辑依赖
com.alibaba.cloud
spring-cloud-starter-dubbo
cn.tedu
csmall-order-service
0.0.1-SNAPSHOT
cn.tedu
csmall-cart-service
0.0.1-SNAPSHOT
cn.tedu
csmall-stock-service
0.0.1-SNAPSHOT
还有就是业务逻辑层实现有修改
之前order模块只是单纯的添加订单信息到数据库 现在我们要在添加订单到数据库之前先删除库存和购物车的信息
@DubboService
@Service
@Slf4j
public class OrderServiceImpl implements IOrderService {
@Autowired
private OrderMapper orderMapper;
// 当前业务逻辑层要调用库存模块的减少库存方法
@DubboReference
private IStockService dubboStockService;
// 当前业务逻辑层要调用购物车模块的删除购物车中商品的方法
@DubboReference
private ICartService dubboCartService;
@Override
public void orderAdd(OrderAddDTO orderAddDTO) {
// 这里新增订单要先减少库存(要调用stock项目的方法)
// dubbo远程调用,减少库存
// 实例化减少库存需要的对象
StockReduceCountDTO stockReduceCountDTO=new StockReduceCountDTO();
// 赋值减少库存数
stockReduceCountDTO.setReduceCount(orderAddDTO.getCount());
// 赋值减少商品编号
stockReduceCountDTO.setCommodityCode(orderAddDTO.getCommodityCode());
// 执行减少库存
dubboStockService.reduceCommodityCount(stockReduceCountDTO);
// 再删除购物车中的商品(要调用cart项目的方法)
dubboCartService.deleteUserCart(orderAddDTO.getUserId(),
orderAddDTO.getCommodityCode());
// 实例化订单对象
Order order=new Order();
// 同名属性复制
BeanUtils.copyProperties(orderAddDTO,order);
orderMapper.insertOrder(order);
log.info("新增订单完成!{}",order);
}
}
别忘了添加@EnableDubbo
@SpringBootApplication
// ↓↓↓↓↓↓↓↓↓↓↓↓
@EnableDubbo
public class CsmallOrderWebapiApplication {
public static void main(String[] args) {
SpringApplication.run(CsmallOrderWebapiApplication.class, args);
}
}
business模块是整个业务的起点
它是单纯的服务消费者,所以不需要在创建两个子项目了
只需添加dubbo依赖调用order的业务逻辑层方法即可
com.alibaba.cloud
spring-cloud-starter-dubbo
cn.tedu
csmall-order-service
0.0.1-SNAPSHOT
application-dev.yml
spring:
application:
name: nacos-business # 定义当前服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 # 指定正在运行的Nacos服务器的位置
dubbo:
protocol:
port: -1 # 设置dubbo服务调用的端口 设置-1表示自动生成,生成规则是从20880开始递增
name: dubbo # 端口名称固定dubbo即可
registry:
address: nacos://localhost:8848 # 表示当前Dubbo的注册中心类型是Nacos,地址是后面的内容
consumer:
check: false # 设置false表示服务启动时,不检查标定的可调用的远程服务是否存在,避免报错
在busuiness模块的业务逻辑层调用order的添加订单的方法
BusinessServiceImpl修改代码如下:
@Service
@Slf4j
public class BusinessServiceImpl implements IBusinessService {
@DubboReference
private IOrderService dubboOrderService;
@Override
public void buy() {
OrderAddDTO orderAddDTO=new OrderAddDTO();
orderAddDTO.setCommodityCode("PC100");
orderAddDTO.setUserId("UU100");
orderAddDTO.setCount(5);
orderAddDTO.setMoney(500);
log.info("要新增的订单信息为:{}",orderAddDTO);
// 远程调用新增订单的方法
dubboOrderService.orderAdd(orderAddDTO);
}
}
别忘了添加@EnableDubbo
@SpringBootApplication
// ↓↓↓↓↓↓↓↓↓↓
@EnableDubbo
public class CsmallBusinessApplication {
public static void main(String[] args) {
SpringApplication.run(CsmallBusinessApplication.class, args);
}
}
启动所有4个项目
进入20000端口测试business模块发起业务调用
如果能够顺利新增订单.表示一切ok
Seata官方文档
Seata
是阿里巴巴提供的SpringCloud组件
Seata是一款开源的分布式解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务
事务的4个特性:ACID特性
原子性
一致性
隔离性
永久性
其中最重要的原子性能保证整体业务运行中所有操作要么都成功,要么都失败
分布式事务和之前学习的Spring声明式事务不同
它不是一个项目的数据库操作,所以不能简单的通过一个数据库事务就完成
每个项目都有自己的数据库事务,那么要想在多个事务的提交过程中在添加支持他们事务就需要Seata
Seata将为用户提供了 AT TCC SAGA 和 XA事务模式,为用户打造一站式分布解决方案
这里以AT模式为例讲解原理
上面的结构和我们的课程中类似,可以方便理解
当account操作失败时,要让已经操作完成的order撤销操作
也要让stoage撤销操作
不使用分布式事务非常难以处理
Seata的At模式格式如下,来解决这个问题
Seata构成
事务协调器TC
事务管理器TM
资源管理器RM
1 事务的发起方(TM)会向事务协调器(TC)申请一个全局事务id,并保存
2 Seata会管理事务中所有相关的参与方的数据源,将数据操作之前和之后的镜像都保存在undo_lug表中,这个表是seata框架规定的,方便提交(commit)或回滚(roll back)
3事务的发起方(TM)会连同全局id一起通过远程调用运行资源管理器(RM)中的方法
4 资源管理器(RM)接受到全局id,并运行指定的方法,将运行的状态同步到事务协调器(TC)
5 如果运行整体没有发生异常,发起方(TM)会通过事务协调器所有分支,将本次事务所有对数据库的影响真正生效
如果有任何一个参与者发生异常,那么都会通知事务协调器,在由事务协调器通知有分支,根据
undo-log表中保存的信息,撤销(回滚)即将正式影响数据库的数据
Seata将为用户提供了 AT TCC SAGA和 XA事务模式
AT模式只能用于数据操作事务
如果事务中有的参与者操作的不是关系型数据库(例如操作Redis)
那么AT模式就不能生效了
这个模式可以实现对数据库之外的信息存储媒介进行回滚操作
只不过这个回滚需要我们自己编写代码
需要为每个业务编写Prepare\Commit\Rollback方法
Prepare编写常规准备 如果整个业务运行无异常运行Commit,如果有异常会自动运行Rollback
缺点是每个业务都需要编写3个对应的方法,代码有冗余 而且业务入侵量大
一般用于修改老版本代码
不用编写象TCC模式那多的方法
但是需要手动编写每个参与者的方向回滚业务逻辑层代码类
开发量大
XA是适用于支持XA协议的数据库,使用的比较少
Releases · seata/seata · GitHub
https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.zip
cart\stock\order模块的pom文件添加下面支持
io.seata
seata-spring-boot-starter
com.github.pagehelper
pagehelper-spring-boot-starter
com.alibaba
fastjson
上述模块还要修改yml文件
application-dev.yaml
seata:
tx-service-group: csmall_group # 分组
service:
vgroup-mapping:
csmall_group: default # 默认at模式
grouplist:
default: localhost:8091
配置中,同一个事务的多个参与者必须在同一个名称的分组下
同时制定相同的seata-server的ip和端口
business模块配置更简单
pom文件只加一个依赖
io.seata
seata-spring-boot-starter
application-dev.yaml和之前的一样
seata:
tx-service-group: csmall_group # 分组
service:
vgroup-mapping:
csmall_group: default # 默认at模式
grouplist:
default: localhost:8091
当前项目模型中,business是事务的发起者
发起者组织事务开始
必须由特定的注解标记业务逻辑层方法(@GlobalTrancsational)
在这个方法运行时会激活Seata的分布式事务管理流程
@Service
@Slf4j
public class BusinessServiceImpl implements IBusinessService {
@DubboReference
private IOrderService dubboOrderService;
// 下面的注解是激活seata分布式事务管理 并开始运行的标记注解
@GlobalTransactional
@Override
public void buy() {
OrderAddDTO orderAddDTO=new OrderAddDTO();
orderAddDTO.setCommodityCode("PC100");
orderAddDTO.setUserId("UU100");
orderAddDTO.setCount(5);
orderAddDTO.setMoney(500);
log.info("要新增的订单信息为:{}",orderAddDTO);
// 远程调用新增订单的方法
dubboOrderService.orderAdd(orderAddDTO);
// 下面来编写代码随机抛出异常
// 注意这个位置发生异常的话,实际上前面已经运行完毕了orderAdd方法
// 我们需要观察随机发生异常时,各个表中的数据 是否能够回滚到方法运行之前的状态
if(Math.random()<0.5){
// 手动抛出自定义异常
throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR,"发送随机异常");
}
}
}
启动所有4个服务
运行knife4j测试
测试business模块,如果能够运行出现成功或出现异常的提示信息
并在数据库中呈现正常运行或回滚的效果,表示一切正常
在windows系统中运行seata可能出现不稳定的情况,重启seata即可解决
Sentil也是阿里巴巴提供的SpringCloud组件
Sentinel 英文意思"哨兵\门卫"
随着微服务的流行 服务和服务之间的稳定性变的越来越重要, Sentinel 以流量为切入点,从流量控制 容断降级 系统负载保护等多个维度保护服务的稳定性
双11,秒杀,12306抢火车票
可以支持显示当前项目各个服务的运行和压力状态,分析出每台服务器处理的秒级别的数据
很多技术可以和Sentinel进行整合,SpringCloud,Dubbo 而且依赖少配置简单
Sentinel支持程序设置各种自定义的规则
Releases · alibaba/Sentinel · GitHub
我们先在库存模块进行限流测试
以stock-webapi库存模块为例
添加依赖pom文件如下
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
application-dev.yml配置Sentinel支持
spring:
application:
name: nacos-stock # 定义当前服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 # 指定正在运行的Nacos服务器的位置
sentinel:
transport:
dashboard: localhost:8080 # Sentinel仪表台的ip:端口
port: 8721 # 是localhost的8721 这个端口真正参与当前项目的限流措施
其中port: 8721这个端口号每个项目都不能一样
配置准备工作完毕
下面要使用注解标记限流的控制层方法
在stock-wenapi中的StockController类中控制器方法前添加注解
代码如下:
@PostMapping("/reduce/count")
@ApiOperation("商品库存减少")
// ↓↓↓↓↓↓↓↓↓↓↓↓
@SentinelResource
public JsonResult reduceCommodityCount(StockReduceCountDTO stockReduceCountDTO){
stockService.reduceCommodityCount(stockReduceCountDTO);
return JsonResult.ok("商品减少库存完成!");
}
运行控制器方法之后
Sentinel的仪表台就能设置这个控制方法的限流规则了
QPS是每秒请求数
并发线程数是同时方法这个方法的线程数量
超出的部分都会被Sentinel限流 快速失败