0,SpringCloud升级,部分组件停用:

1,Eureka停用,可以使用zk作为服务注册中心

2,服务调用,Ribbon准备停更,代替为LoadBalance

3,Feign改为OpenFeign

4,Hystrix停更,改为resilence4j

  1. 或者阿里巴巴的sentienl

5.Zuul改为gateway

6,服务配置Config改为 Nacos

7,服务总线Bus改为Nacos

环境搭建:

1,创建父工程,pom依赖

  1. ....

2,创建子模块,pay模块

SpringCloud - 图3

1,子模块名字:

  1. cloud_pay_8001

2,pom依赖

3,创建application.yml

  1. server:
  2. port: 8001
  3. spring:
  4. application:
  5. name: cloud-payment-service
  6. datasource:
  7. # 当前数据源操作类型
  8. type: com.alibaba.druid.pool.DruidDataSource
  9. # mysql驱动类
  10. driver-class-name: com.mysql.cj.jdbc.Driver
  11. url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=
  12. UTF-8&useSSL=false&serverTimezone=GMT%2B8
  13. username: root
  14. password: root
  15. mybatis:
  16. mapper-locations: classpath*:mapper/*.xml
  17. type-aliases-package: com.eiletxie.springcloud.entities
  18. 它一般对应我们的实体类所在的包,这个时候会自动取对应包中不包括包名的简单类名作为包括包名的别名。多个package之间可以用逗号或者分号等来进行分隔(value的值一定要是包的全)

4,主启动类

  1. ....

5,业务类

1,sql

SpringCloud - 图4

2,实体类

SpringCloud - 图5

3,.entity类

SpringCloud - 图6

4,dao层:

SpringCloud - 图7

5,mapper配置文件类

  1. **在resource下,创建mapper/PayMapper.xml**

SpringCloud - 图8

6,写service和serviceImpl

SpringCloud - 图9

SpringCloud - 图10

7,controller

SpringCloud - 图11

SpringCloud - 图12

3,热部署:

SpringCloud - 图13

SpringCloud - 图14

…..

…..

….

4,order模块

SpringCloud - 图15

1,pom

2,yml配置文件

SpringCloud - 图16

3,主启动类

4.复制pay模块的实体类,entity类

5,写controller类

  1. 因为这里是消费者类,主要是消费,那么就没有servicedao,需要调用pay模块的方法
  2. 并且这里还没有微服务的远程调用,那么如果要调用另外一个模块,则需要使用基本的api调用

使用RestTemplate调用pay模块,

  1. ![](.%5C%E5%9B%BE%E7%89%87%5Corder%E6%A8%A1%E5%9D%972.png#alt=)

SpringCloud - 图17

  1. restTemplate注入到容器

SpringCloud - 图18

编写controller:

SpringCloud - 图19

5,重构,

新建一个模块,将重复代码抽取到一个公共模块中

1,创建commons模块

2,抽取公共pom

SpringCloud - 图20

3,entity和实体类放入commons中

SpringCloud - 图21

4,使用mavne,将commone模块打包(install),

  1. 其他模块引入commons

2,服务注册与发现

6,Eureka:

前面我们没有服务注册中心,也可以服务间调用,为什么还要服务注册?

当服务很多时,单靠代码手动管理是很麻烦的,需要一个公共组件,统一管理多服务,包括服务是否正常运行,等

Eureka用于服务注册,目前官网已经停止更新

  1. ![](.%5C%E5%9B%BE%E7%89%87%5CEureka%E7%9A%841.png#alt=)

SpringCloud - 图22

SpringCloud - 图23

SpringCloud - 图24

单机版eureka:

1,创建项目cloud_eureka_server_7001

2,引入pom依赖

  1. eurka最新的依赖变了

SpringCloud - 图25

3,配置文件:

SpringCloud - 图26

4,主启动类

SpringCloud - 图27

5,此时就可以启动当前项目了

6,其他服务注册到eureka:

比如此时pay模块加入eureka:

1.主启动类上,加注解,表示当前是eureka客户端

SpringCloud - 图28

2,修改pom,引入

SpringCloud - 图29

3,修改配置文件:

SpringCloud - 图30

4,pay模块重启,就可以注册到eureka中了

order模块的注册是一样的

集群版eureka:

集群原理:

SpringCloud - 图31

  1. 1,就是pay模块启动时,注册自己,并且自身信息也放入eureka
  2. 2.order模块,首先也注册自己,放入信息,当要调用pay时,先从eureka拿到pay的调用地址
  3. 3.通过HttpClient调用
  4. 并且还会缓存一份到本地,每30秒更新一次

SpringCloud - 图32

集群构建原理:

  1. 互相注册

SpringCloud - 图33

构建新erueka项目

名字:cloud_eureka_server_7002

1,pom文件:
  1. 粘贴7001的即可

2,配置文件:
  1. 在写配置文件前,修改一下主机的hosts文件

SpringCloud - 图34

首先修改之前的7001的eureka项目,因为多个eureka需要互相注册

SpringCloud - 图35

然后修改7002

  1. **7002也是一样的,只不过端口和地址改一下**

3,主启动类:
  1. 复制7001的即可

4,然后启动7001,7002即可

SpringCloud - 图36

将pay,order模块注册到eureka集群中:

1,只需要修改配置文件即可:

SpringCloud - 图37

2,两个模块都修改上面的都一样即可
  1. 然后启动两个模块
  2. 要先启动7001,7002,然后是pay模块8001,然后是order(80)

3,将pay模块也配置为集群模式:

0,创建新模块,8002

  1. 名称: cloud_pay_8002

1,pom文件,复制8001的

2,pom文件复制8001的

3,配置文件复制8001的

  1. 端口修改一下,改为8002
  2. 服务名称不用改,用一样的

4.主启动类,复制8001的

5,mapper,service,controller都复制一份

  1. 然后就启动服务即可
  2. 此时访问order模块,发现并没有负载均衡到两个pay,模块中,而是只访问8001
  3. 虽然我们是使用RestTemplate访问的微服务,但是也可以负载均衡的
  4. ![](.%5C%E5%9B%BE%E7%89%87%5CEureka%E7%9A%8418.png#alt=)

注意这样还不可以,需要让RestTemplate开启负载均衡注解,还可以指定负载均衡算法,默认轮询

SpringCloud - 图38

4,修改服务主机名和ip在eureka的web上显示

比如修改pay模块

1,修改配置文件:

SpringCloud - 图39

5,eureka服务发现:

SpringCloud - 图40

以pay模块为例

1,首先添加一个注解,在controller中

SpringCloud - 图41

SpringCloud - 图42

2,在主启动类上添加一个注解

SpringCloud - 图43

然后重启8001.访问/payment/discovery

6,Eureka自我保护:

SpringCloud - 图44

SpringCloud - 图45

SpringCloud - 图46

SpringCloud - 图47

eureka服务端配置:

SpringCloud - 图48

SpringCloud - 图49

  1. **设置接受心跳时间间隔**

客户端(比如pay模块):

SpringCloud - 图50

此时启动erueka和pay.此时如果直接关闭了pay,那么erueka会直接删除其注册信息

7,Zookeeper服务注册与发现:

1,启动zk,到linux上

2,创建新的pay模块,

单独用于注册到zk中

名字 : cloud_pay_8003

1,pom依赖

2,配置文件

SpringCloud - 图51

3,主启动类

SpringCloud - 图52

4,controller

SpringCloud - 图53

5,然后就可以启动

此时启动,会报错,因为jar包与我们的zk版本不匹配

解决:
修改pom文件,改为与我们zk版本匹配的jar包

SpringCloud - 图54

此时8003就注册到zk中了

  1. 我们在zk上注册的node是临时节点,当我们的服务一定时间内没有发送心跳
  2. 那么zk就会`将这个服务的node删除了

这里测试,就不写service与dao什么的了

3,创建order消费模块注册到zk

1,创建项目

名字: cloud_order_zk_80

2,pom

3,配置文件

SpringCloud - 图55

4主启动类:

SpringCloud - 图56

5,RestTemolate

SpringCloud - 图57

6,controller

SpringCloud - 图58

然后启动即可注册到zk

8,集群版zk注册:

只需要修改配置文件:

SpringCloud - 图59

这个connect-string指定多个zk地址即可

connect-string: 1.2.3.4,2.3.4.5

8,Consul:

SpringCloud - 图60

SpringCloud - 图61

1,按照consul

需要下载一个安装包

SpringCloud - 图62

启动是一个命令行界面,需要输入consul agen-dev启动

2,创建新的pay模块,8006

1,项目名字

cloud_consule_pay_8006

2,pom依赖

3,配置文件

SpringCloud - 图63

4,主启动类

SpringCloud - 图64

5,controller

SpringCloud - 图65

6,启动服务

3,创建新order模块

cloud-consul-order-80

1,pom文件

2,配置文件

SpringCloud - 图66

3,主启动类

SpringCloud - 图67

4,RestTemplate注册

配置类注册

5,controller

SpringCloud - 图68

6,启动服务,测试

9,三个注册中心的异同:

SpringCloud - 图69

SpringCloud - 图70

SpringCloud - 图71

3,服务调用

10,Ribbon负载均衡:

SpringCloud - 图72

Ribbon目前也进入维护,基本上不准备更新了

SpringCloud - 图73

进程内LB(本地负载均衡)

SpringCloud - 图74

集中式LB(服务端负载均衡)

SpringCloud - 图75

区别

SpringCloud - 图76

Ribbon就是负载均衡+RestTemplate

SpringCloud - 图77

SpringCloud - 图78

SpringCloud - 图79

使用Ribbon:

1,默认我们使用eureka的新版本时,它默认集成了ribbon:

SpringCloud - 图80

这个starter中集成了reibbon了

2,我们也可以手动引入ribbon

放到order模块中,因为只有order访问pay时需要负载均衡

SpringCloud - 图81

3,RestTemplate类:

SpringCloud - 图82

SpringCloud - 图83

  1. RestTemplate的:
  2. xxxForObject()方法,返回的是响应体中的数据
  3. xxxForEntity()方法.返回的是entity对象,这个对象不仅仅包含响应体数据,还包含响应体信息(状态码等)

Ribbon常用负载均衡算法:

IRule接口,Riboon使用该接口,根据特定算法从所有服务中,选择一个服务,

Rule接口有7个实现类,每个实现类代表一个负载均衡算法

SpringCloud - 图84

使用Ribbon:

这里使用eureka的那一套服务

SpringCloud - 图85

也就是不能放在主启动类所在的包及子包下

1,修改order模块

2,额外创建一个包

SpringCloud - 图86

3,创建配置类,指定负载均衡算法

SpringCloud - 图87

4,在主启动类上加一个注解

SpringCloud - 图88

表示,访问CLOUD_pAYMENT_SERVICE的服务时,使用我们自定义的负载均衡算法

自定义负载均衡算法:

1,ribbon的轮询算法原理

SpringCloud - 图89

SpringCloud - 图90

2,自定义负载均衡算法:

1,给pay模块(8001,8002),的controller方法添加一个方法,返回当前节点端口

SpringCloud - 图91

SpringCloud - 图92

2,修改order模块

去掉@LoadBalanced

SpringCloud - 图93

3,自定义接口

SpringCloud - 图94

  1. 具体的算法在实现类中实现

4,接口实现类

SpringCloud - 图95

SpringCloud - 图96

5,修改controller:

SpringCloud - 图97

SpringCloud - 图98

6,启动服务,测试即可

11,OpenFeign

SpringCloud - 图99

是一个声明式的web客户端,只需要创建一个接口,添加注解即可完成微服务之间的调用

SpringCloud - 图100

就是A要调用B,Feign就是在A中创建一个一模一样的B对外提供服务的的接口,我们调用这个接口,就可以服务到B

Feign与OpenFeign区别

SpringCloud - 图101

使用OpenFeign

  1. 之前的服务间调用,我们使用的是ribbon+RestTemplate
  2. 现在改为使用Feign

1,新建一个order项目,用于feign测试

名字cloud_order_feign-80

2,pom文件

3,配置文件

SpringCloud - 图102

4,主启动类

SpringCloud - 图103

5,fegin需要调用的其他的服务的接口

SpringCloud - 图104

6,controller

SpringCloud - 图105

7测试:

启动两个erueka(7001,7002)

启动两个pay(8001,8002)

启动当前的order模块

Feign默认使用ribbon实现负载均衡

OpenFeign超时机制:

OpenFeign默认等待时间是1秒,超过1秒,直接报错

1,设置超时时间,修改配置文件:

因为OpenFeign的底层是ribbon进行负载均衡,所以它的超时时间是由ribbon控制

SpringCloud - 图106

OpenFeign日志:

SpringCloud - 图107

OpenFeign的日志级别有:
SpringCloud - 图108

1,使用OpenFeign的日志:

实现在配置类中添加OpenFeign的日志类

SpringCloud - 图109

2,为指定类设置日志级别:

SpringCloud - 图110

配置文件中:

SpringCloud - 图111

3,启动服务即可

4,服务降级:

12,Hystrix服务降级

SpringCloud - 图112

SpringCloud - 图113

SpringCloud - 图114

hystrix中的重要概念:

1,服务降级

比如当某个服务繁忙,不能让客户端的请求一直等待,应该立刻返回给客户端一个备选方案

2,服务熔断

当某个服务出现问题,卡死了,不能让用户一直等待,需要关闭所有对此服务的访问

  1. **然后调用服务降级**

3,服务限流

限流,比如秒杀场景,不能访问用户瞬间都访问服务器,限制一次只可以有多少请求

使用hystrix,服务降级:

1,创建带降级机制的pay模块 :

名字: cloud-hystrix-pay-8007

2,pom文件

3,配置文件

SpringCloud - 图115

4,主启动类

SpringCloud - 图116

5,service

SpringCloud - 图117

6controller

SpringCloud - 图118

7,先测试:
  1. 此时使用压测工具,并发20000个请求,请求会延迟的那个方法,
  2. 压测中,发现,另外一个方法并没有被压测,但是我们访问它时,却需要等待
  3. 这就是因为被压测的方法它占用了服务器大部分资源,导致其他请求也变慢了

8,先不加入hystrix,

2,创建带降级的order模块:

1,名字: cloud-hystrix-order-80

2,pom

3,配置文件

SpringCloud - 图119

4,主启动类

SpringCloud - 图120

5,远程调用pay模块的接口:

SpringCloud - 图121

6,controller:

SpringCloud - 图122

7,测试
  1. 启动order模块,访问pay
  2. 再次压测2万并发,发现order访问也变慢了

SpringCloud - 图123

解决:

SpringCloud - 图124

SpringCloud - 图125

3,配置服务降级:

1,修改pay模块

1,为service的指定方法(会延迟的方法)添加@HystrixCommand注解

SpringCloud - 图126

2,主启动类上,添加激活hystrix的注解

SpringCloud - 图127

3,触发异常

SpringCloud - 图128

SpringCloud - 图129可以看到,也触发了降级

2,修改order模块,进行服务降级

一般服务降级,都是放在客户端(order模块),

SpringCloud - 图130

1,修改配置文件:

SpringCloud - 图131

2,主启动类添加直接,启用hystrix:

SpringCloud - 图132

3,修改controller,添加降级方法什么的

SpringCloud - 图133

4,测试

启动pay模块,order模块,

注意:,这里pay模块和order模块都开启了服务降级

  1. 但是order这里,设置了1.5秒就降级,所以访问时,一定会降级

4,重构:

上面出现的问题:
1,降级方法与业务方法写在了一块,耦合度高

  1. 2.每个业务方法都写了一个降级方法,重复代码多

解决重复代码的问题:

配置一个全局的降级方法,所有方法都可以走这个降级方法,至于某些特殊创建,再单独创建方法

1,创建一个全局方法

SpringCloud - 图134

2,使用注解指定其为全局降级方法(默认降级方法)

SpringCloud - 图135

SpringCloud - 图136

3,业务方法使用默认降级方法:

SpringCloud - 图137

4,测试:

SpringCloud - 图138

解决代码耦合度的问题:

修改order模块,这里开始,pay模块就不服务降级了,服务降级写在order模块即可

1,Payservice接口是远程调用pay模块的,我们这里创建一个类实现service接口,在实现类中统一处理异常

SpringCloud - 图139

2,修改配置文件:添加:

SpringCloud - 图140

3,让PayService的实现类生效:

SpringCloud - 图141

  1. 它的运行逻辑是:
  2. 当请求过来,首先还是通过Feign远程调用pay模块对应的方法
  3. 但是如果pay模块报错,调用失败,那么就会调用PayMentFalbackService类的
  4. 当前同名的方法,作为降级方法

4,启动测试

启动order和pay正常访问—ok

此时将pay服务关闭,order再次访问

SpringCloud - 图142

可以看到,并没有报500错误,而是降级访问实现类的同名方法

这样,即使服务器挂了,用户要不要一直等待,或者报错

问题:

  1. **这样虽然解决了代码耦合度问题,但是又出现了过多重复代码的问题,每个方法都有一个降级方法**

使用服务熔断:

SpringCloud - 图143

比如并发达到1000,我们就拒绝其他用户访问,在有用户访问,就访问降级方法

SpringCloud - 图144

1,修改前面的pay模块

1,修改Payservice接口,添加服务熔断相关的方法:

SpringCloud - 图145

这里属性整体意思是:
10秒之内(窗口,会移动),如果并发超过10个,或者10个并发中,失败了6个,就开启熔断器

SpringCloud - 图146

IdUtil是Hutool包下的类,这个Hutool就是整合了所有的常用方法,比如UUID,反射,IO流等工具方法什么的都整合了

SpringCloud - 图147

  1. 断路器的打开和关闭,是按照一下5步决定的
  2. 1,并发此时是否达到我们指定的阈值
  3. 2,错误百分比,比如我们配置了60%,那么如果并发请求中,10次有6次是失败的,就开启断路器
  4. 3,上面的条件符合,断路器改变状态为open(开启)
  5. 4,这个服务的断路器开启,所有请求无法访问
  6. 5,在我们的时间窗口期,期间,尝试让一些请求通过(半开状态),如果请求还是失败,证明断路器还是开启状态,服务没有恢复
  7. 如果请求成功了,证明服务已经恢复,断路器状态变为close关闭状态

2,修改controller

添加一个测试方法;

SpringCloud - 图148

3,测试:

启动pay,order模块

多次访问,并且错误率超过60%:

SpringCloud - 图149

此时服务熔断,此时即使访问正确的也会报错:

SpringCloud - 图150

但是,当过了几秒后,又恢复了

  1. 因为在10秒窗口期内,它自己会尝试接收部分请求,发现服务可以正常调用,慢慢的当错误率低于60%,取消熔断

Hystrix所有可配置的属性:

全部在这个方法中记录,以成员变量的形式记录,

  1. 以后需要什么属性,查看这个类即可

SpringCloud - 图151

总结:

SpringCloud - 图152

当断路器开启后:

  1. ![](.%5C%E5%9B%BE%E7%89%87%5CHystrix%E7%9A%8444.png#alt=)

其他参数:

SpringCloud - 图153

SpringCloud - 图154

SpringCloud - 图155

SpringCloud - 图156

SpringCloud - 图157

熔断整体流程:

  1. 1请求进来,首先查询缓存,如果缓存有,直接返回
  2. 如果缓存没有,--->2
  3. 2,查看断路器是否开启,如果开启的,Hystrix直接将请求转发到降级返回,然后返回
  4. 如果断路器是关闭的,
  5. 判断线程池等资源是否已经满了,如果已经满了
  6. 也会走降级方法
  7. 如果资源没有满,判断我们使用的什么类型的Hystrix,决定调用构造方法还是run方法
  8. 然后处理请求
  9. 然后Hystrix将本次请求的结果信息汇报给断路器,因为断路器此时可能是开启的
  10. (因为断路器开启也是可以接收请求的)
  11. 断路器收到信息,判断是否符合开启或关闭断路器的条件,
  12. 如果本次请求处理失败,又会进入降级方法
  13. 如果处理成功,判断处理是否超时,如果超时了,也进入降级方法
  14. 最后,没有超时,则本次请求处理成功,将结果返回给controller

Hystrix服务监控:

HystrixDashboard

SpringCloud - 图158

2,使用HystrixDashboard:

1,创建项目:

名字: cloud_hystrixdashboard_9001

2,pom文件

3,配置文件

SpringCloud - 图159

4,主启动类

SpringCloud - 图160

5,修改所有pay模块(8001,8002,8003…)

他们都添加一个pom依赖:

SpringCloud - 图161

之前的pom文件中都添加过了,这个是springboot的监控组件

6,启动9001即可
  1. 访问: **localhost:9001/hystrix**

7,注意,此时仅仅是可以访问HystrixDashboard,并不代表已经监控了8001,8002
  1. 如果要监控,还需要配置:(8001为例)

8001的主启动类添加:

SpringCloud - 图162

其他8002,8003都是一样的

8,到此,可以启动服务

启动7001,8001,9001

然后在web界面,指定9001要监控8001:

SpringCloud - 图163

SpringCloud - 图164

SpringCloud - 图165

SpringCloud - 图166

SpringCloud - 图167

SpringCloud - 图168

SpringCloud - 图169

5,服务网关:

zuul停更了,

13,GateWay

SpringCloud - 图170

SpringCloud - 图171

gateway之所以性能号,因为底层使用WebFlux,而webFlux底层使用netty通信(NIO)

SpringCloud - 图172

GateWay的特性:

SpringCloud - 图173

GateWay与zuul的区别:

SpringCloud - 图174

zuul1.x的模型:

SpringCloud - 图175

SpringCloud - 图176

什么是webflux:

是一个非阻塞的web框架,类似springmvc这样的

SpringCloud - 图177

GateWay的一些概念:

1,路由:

SpringCloud - 图178

就是根据某些规则,将请求发送到指定服务上

2,断言:

SpringCloud - 图179

就是判断,如果符合条件就是xxxx,反之yyyy

3,过滤:

SpringCloud - 图180

  1. **路由前后,过滤请求**

GateWay的工作原理:

SpringCloud - 图181

SpringCloud - 图182

使用GateWay:

想要新建一个GateWay的项目

名字: cloud_gateway_9527

1,pom

2,配置文件

SpringCloud - 图183

3,主启动类

SpringCloud - 图184

4,针对pay模块,设置路由:

SpringCloud - 图185

SpringCloud - 图186

修改GateWay模块(9527)的配置文件:

SpringCloud - 图187

这里表示,

  1. 当访问localhost:9527/payment/get/1时,
  2. 路由到localhost:8001/payment/get/1

5,开始测试

启动7001,8001,9527

  1. 如果启动GateWay报错
  2. 可能是GateWay模块引入了web和监控的starter依赖,需要移除

访问:

  1. localhost:9527/payment/get/1

SpringCloud - 图188

6,GateWay的网关配置,

  1. **GateWay的网关配置,除了支持配置文件,还支持硬编码方式**

7使用硬编码配置GateWay:

创建配置类:

SpringCloud - 图189

8,然后重启服务即可

重构:

上面的配置虽然首先了网关,但是是在配置文件中写死了要路由的地址

现在需要修改,不指定地址,而是根据微服务名字进行路由,我们可以在注册中心获取某组微服务的地址

需要:

  1. 1eureka,2pay模块

修改GateWay模块的配置文件:

SpringCloud - 图190

然后就可以启动微服务.测试

Pridicate断言:

SpringCloud - 图191

我们之前在配置文件中配置了断言:

SpringCloud - 图192

这个断言表示,如果外部访问路径是指定路径,就路由到指定微服务上

可以看到,这里有一个Path,这个是断言的一种,断言的类型:

SpringCloud - 图193

  1. After:
  2. 可以指定,只有在指定时间后,才可以路由到指定微服务

SpringCloud - 图194

  1. 这里表示,只有在2020年的221155137秒之后,访问才可以路由
  2. 在此之前的访问,都会报404

如何获取当前时区?**

SpringCloud - 图195

  1. before:
  2. after类似,他说在指定时间之前的才可以访问
  3. between:
  4. 需要指定两个时间,在他们之间的时间才可以访问

SpringCloud - 图196

  1. cookie:
  2. 只有包含某些指定cookie(key,value),的请求才可以路由

SpringCloud - 图197

SpringCloud - 图198

  1. Header:
  2. 只有包含指定请求头的请求,才可以路由

SpringCloud - 图199

SpringCloud - 图200

测试:
SpringCloud - 图201

  1. host:
  2. 只有指定主机的才可以访问,
  3. 比如我们当前的网站的域名是www.aa.com
  4. 那么这里就可以设置,只有用户是www.aa.com的请求,才进行路由

SpringCloud - 图202

SpringCloud - 图203

SpringCloud - 图204

SpringCloud - 图205

可以看到,如果带了域名访问,就可以,但是直接访问ip地址.就报错了

  1. method:
  2. 只有指定请求才可以路由,比如get请求...

SpringCloud - 图206

  1. path:
  2. 只有访问指定路径,才进行路由
  3. 比如访问,/abc才路由

SpringCloud - 图207

  1. Query:
  2. 必须带有请求参数才可以访问

SpringCloud - 图208

Filter过滤器:

SpringCloud - 图209

生命周期:

在请求进入路由之前,和处理请求完成,再次到达路由之前

种类:

SpringCloud - 图210

GateWayFilter,单一的过滤器

与断言类似,比如闲置,请求头,只有特定的请求头才放行,反之就过滤:

SpringCloud - 图211

GlobalFilter,全局过滤器:

自定义过滤器:

实现两个接口

SpringCloud - 图212

  1. **然后启动服务,即可,因为过滤器通过@COmponet已经加入到容器了**

SpringCloud - 图213

SpringCloud - 图214

6,服务配置:

Spring Config分布式配置中心:

微服务面临的问题

  1. 可以看到,每个微服务都需要一个配置文件,并且,如果有几个微服务都需要连接数据库
  2. 那么就需要配4次数据库相关配置,并且当数据库发生改动,那么需要同时修改4个微服务的配置文件才可以

所以有了springconfig配置中心

SpringCloud - 图215

SpringCloud - 图216

SpringCloud - 图217

SpringCloud - 图218

使用配置中心:

0,使用github作为配置中心的仓库:

初始化git环境:

SpringCloud - 图219

1,新建config模块:

名字: cloud-config-3344

2,pom

3,配置文件

SpringCloud - 图220

4,主启动类

SpringCloud - 图221

5,修改hosts:

SpringCloud - 图222

6,配置完成

测试,3344是否可以从github上获取配置

启动3344 (要先启动eureka)

SpringCloud - 图223

它实际上就是,读取到配置文件中的GitHub的地址,然后拼接上/master/config-dev.yml

7,读取配置文件的规则:

SpringCloud - 图224

2,

SpringCloud - 图225

这里默认会读取master分支,因为我们配置文件中配置了

SpringCloud - 图226

3

SpringCloud - 图227

注意,这个方式读取到的配置是json格式的

所有规则:

SpringCloud - 图228

2,创建配置中心客户端:

1,创建config客户端项目

名字: cloud-config-client-3355

2,pom

3,配置文件

注意这个配置文件就不是application.yml

  1. 而是bootstrap.yml

这个配置文件的作用是,先到配置中心加载配置,然后加载到application.yml中

SpringCloud - 图229

SpringCloud - 图230

4,主启动类:

SpringCloud - 图231

5,controller类

就是上面提到的,以rest风格将配置对外暴露

SpringCloud - 图232

SpringCloud - 图233

如果客户端运行正常,就会读取到github上配置文件的,config.info下的配置

6,测试:

启动3344,3355

  1. 访问3355 /configInfo

SpringCloud - 图234

7,问题::

  1. 上面3355确实获取到了配置文件,但是如果此时配置文件修改了,3355是获取不到的
  2. 3344可以实时获取到最新配置文件,但是3355却获取不到
  3. 除非重启服务

8,实现动态刷新:

1,修改3355,添加一个pom依赖:

SpringCloud - 图235

2,修改配置文件,添加一个配置:

SpringCloud - 图236

3,修改controller:

SpringCloud - 图237

4,此时重启服务

此时3355还不可以动态获取

因为此时,还需要外部发送post请求通知3355

SpringCloud - 图238

此时在刷新3355,发现可以获取到最新的配置文件了,这就实现了动态获取配置文件,因为3355并没有重启

具体流程就是:

  1. 我们启动好服务后
  2. 运维人员,修改了配置文件,然后发送一个post请求通知3355
  3. 3355就可以获取最新配置文件

问题:

  1. 如果有多个客户端怎么办(3355,3356,3357.....)
  2. 虽然可以使用shell脚本,循环刷新
  3. 但是,可不可以使用广播,一次通知??
  4. 这些springconfig做不到,需要使用springcloud Bus消息总线

消息总线:

SpringCloud Bus:

SpringCloud - 图239

SpringCloud - 图240

SpringCloud - 图241

注意,这里年张图片,就代表两种广播方式

  1. 1: **它是Bus直接通知给其中一个客户端,由这个客户端开始蔓延,传播给其他所有客户端**
  2. 2: 它**是通知给配置中心的服务端,有服务端广播给所有客户端**

为什么被称为总线?

SpringCloud - 图242

  1. 就是通过消息队列达到广播的效果
  2. 我们要广播每个消息时,主要放到某个topic中,所有监听的节点都可以获取到

使用Bus:

1,配置rabbitmq环境:

SpringCloud - 图243

2,之前只有一个配置中心客户端,这里在创建一个

  1. **复制3355即可,创建为3366**

SpringCloud - 图244

全部复制3355的即可

2,使用Bus实现全局广播

Bus广播有两种方式:

  1. 就是上面两个图片的两种方式

SpringCloud - 图245

这两种方式,第二种跟合适,因为:

  1. 第一种的缺点:

SpringCloud - 图246

配置第二种方式:

1,配置3344(配置中心服务端):

1,修改配置文件:

SpringCloud - 图247

2,添加pom

springboot的监控组件,和消息总线

SpringCloud - 图248

SpringCloud - 图249

2,修改3355(配置中心的客户端)

1,pom:

SpringCloud - 图250

SpringCloud - 图251

2,配置文件:

注意配置文件的名字,要改为bootstrap.yml

SpringCloud - 图252

SpringCloud - 图253

3,修改3366(也是配置中心的客户端)
  1. 修改与3355是一模一样的

4,测试

启动7001,3344,3355,3366

此时修改GitHub上的配置文件

此时只需要刷新3344,即可让3355,3366动态获取最新的配置文件

SpringCloud - 图254

其原理就是:

SpringCloud - 图255

所有客户端都监听了一个rabbitMq的topic,我们将信息放入这个topic,所有客户端都可以送到,从而实时更新

配置定点通知

  1. 就是只通知部分服务,比如只通知3355,不通知3366

SpringCloud - 图256

SpringCloud - 图257

只通知3355

SpringCloud - 图258

  1. ![](.%5C%E5%9B%BE%E7%89%87%5CBus%E7%9A%8412.png#alt=)

可以看到,实际上就是通过微服务的名称+端口号进行指定

8,消息驱动:

Spring Cloud Stream:

  1. 现在一个很项目可能分为三部分:
  2. 前端--->后端---->大数据
  3. 而后端开发使用消息中间件,可能会使用RabbitMq
  4. 而大数据开发,一般都是使用Kafka,
  5. 那么一个项目中有多个消息中间件,对于程序员,因为人员都不友好

而Spring Cloud Stream就类似jpa,屏蔽底层消息中间件的差异,程序员主要操作Spring Cloud Stream即可

  1. 不需要管底层是kafka还是rabbitMq

SpringCloud - 图259

什么是Spring Cloud Stream

SpringCloud - 图260

SpringCloud - 图261

SpringCloud - 图262

SpringCloud - 图263

Spring Cloud Stream是怎么屏蔽底层差异的?

SpringCloud - 图264

绑定器:

SpringCloud - 图265

SpringCloud - 图266

SpringCloud - 图267

Spring Cloud Streamd 通信模式:

SpringCloud - 图268SpringCloud - 图269

Spring Cloud Stream的业务流程:

SpringCloud - 图270

SpringCloud - 图271

SpringCloud - 图272

  1. 类似flume中的channel,source,sink 估计是借鉴(抄袭)的
  2. source用于获取数据(要发送到mq的数据)
  3. channel类似SpringCloudStream中的中间件,用于存放source接收到的数据,或者是存放binder拉取的数据

常用注解和api:

SpringCloud - 图273

使用SpringCloudStream:

需要创建三个项目,一个生产者,两个消费者

SpringCloud - 图274

1,创建生产者

1,pom

2,配置文件

SpringCloud - 图275

SpringCloud - 图276

3,主启动类

SpringCloud - 图277

4,service和实现类

service定义发送消息

SpringCloud - 图278

SpringCloud - 图279

这里,就会调用send方法,将消息发送给channel,

  1. **然后channel将消费发送给binder,然后发送到rabbitmq中**

5,controller

SpringCloud - 图280

6,可以测试

启动rabbitmq

启动7001,8801

  1. 确定8801后,会在rabbitmq中创建一个Exchange,就是我们配置文件中配置的exchange

访问8801的/sendMessage

创建消费者:

1,pom文件

2,配置文件

这里排版一点问题

input就表示,当前服务是一个消费者,需要消费消息,下面就是指定消费哪个Exchange中的消息

SpringCloud - 图281

SpringCloud - 图282

3,主启动类

SpringCloud - 图283

4,业务类(消费数据)

SpringCloud - 图284

生产者发送消息时,使用send方法发送,send方法发送的是一个个Message,里面封装了数据

5,测试:

启动7001.8801.8802

此时使用生产者生产消息

SpringCloud - 图285

可以看到,消费者已经接收到消息了

创建消费者2

创建8803,

与8802创建一模一样,就不写了

创建8803主要是为了演示重复消费等问题

….

重复消费问题:

此时启动7001.8801.8802.8803

此时生产者生产一条消息

但是此时查询消费者,发现8802,8803都消费到了同一条数据

SpringCloud - 图286

SpringCloud - 图287

1,自定义分组

修改8802,8803的配置文件

SpringCloud - 图288

SpringCloud - 图289

现在将8802,8803都分到了A组

然后去重启02,03

然后此时生产者生产两条消息

SpringCloud - 图290

SpringCloud - 图291

SpringCloud - 图292

可以看到,每人只消费了一条消息,并且没有重复消费

持久化问题:

就是当服务挂了,怎么消费没有消费的数据??

这里,先将8802移除A组,

  1. 然后将02,03服务关闭

此时生产者开启,发送3条消息

  1. 此时重启02,03
  2. 可以看到,当02退出A组后,它就获取不到在它宕机的时间段内的数据
  3. 但是03重启后,直接获取到了宕机期间它没有消费的数据,并且消费了

总结:
也就是,当我们没有配置分组时,会出现消息漏消费的问题

  1. 而配置分组后,我们可以自动获取未消费的数据

9,链路追踪:

Spring Cloud Sleuth

sleuth要解决的问题:

SpringCloud - 图293

而来sleuth就是用于追踪每个请求的整体链路

SpringCloud - 图294

使用sleuth:

1,安装zipkin:

SpringCloud - 图295

运行jar包

  1. java -jar xxxx.jar

然后就可以访问web界面, 默认zipkin监听的端口是9411

  1. localhost:9411/zipkin/

SpringCloud - 图296

一条链路完整图片:

SpringCloud - 图297

精简版:

SpringCloud - 图298

可以看到,类似链表的形式

2,使用sleuth:

不需要额外创建项目,使用之前的8001和order的80即可

1,修改8001

引入pom:

SpringCloud - 图299

这个包虽然叫zipkin但是,里面包含了zpikin与sleuth

修改配置文件:

SpringCloud - 图300

2,修改80

添加pom

与上面是一样的

添加配置:

与上面也是一样的

3,测试:

启动7001.8001,80,9411

SpringCloud - 图301

10,Spring CloudAlibaba:

之所以有Spring CloudAlibaba,是因为Spring Cloud Netflix项目进入维护模式

  1. **也就是,就不是不更新了,不会开发新组件了**
  2. **所以,某些组件都有代替版了,比如RibbonLoadbalancer代替,等等**

支持的功能

SpringCloud - 图302

几乎可以将之前的Spring Cloud代替

具体组件:
SpringCloud - 图303

Nacos:

服务注册和配置中心的组合

  1. Nacos=erueka+config+bus

安装Nacos:

需要java8 和 Mavne

1,到github上下载安装包

  1. 解压安装包

2,启动Nacos

  1. bin下,进入cod
  2. ./startup.cmd

3,访问Nacos

  1. Nacos默认监听8848
  2. localhost:8848/nacos
  3. 账号密码:默认都是nacos

使用Nacos:

新建pay模块

  1. **现在不需要额外的服务注册模块了,Nacos单独启动了**

名字: cloudalibaba-pay-9001

1,pom

父项目管理alibaba的依赖:

SpringCloud - 图304

SpringCloud - 图305

9001的pom:

  1. 另外一个文件.....

2,配置文件

SpringCloud - 图306

3,启动类

SpringCloud - 图307

4,controller:

SpringCloud - 图308

5,测试

启动9001

然后查看Nacos的web界面,可以看到9001已经注册成功

创建其他Pay模块

  1. 额外在创建9002,9003
  2. 直接复制上面的即可

创建order模块

名字: cloudalibaba-order-83

1,pom

为什么Nacos支持负载均衡?

  1. Nacos直接集成了Ribon,所以有负载均衡

2,配置文件

SpringCloud - 图309

这个server-url的作用是,我们在controller,需要使用RestTempalte远程调用9001,

  1. **这里是指定9001的地址**

3,主启动类

SpringCloud - 图310

4,编写配置类

  1. 因为Naocs要使用Ribbon进行负载均衡,那么就需要使用RestTemplate

SpringCloud - 图311

5,controller:

SpringCloud - 图312

6,测试

启动83,访问9001,9002,可以看到,实现了负载均衡

Nacos与其他服务注册的对比

Nacos它既可以支持CP,也可以支持AP,可以切换

SpringCloud - 图313

SpringCloud - 图314

下面这个curl命令,就是切换模式

使用Nacos作为配置中心:

SpringCloud - 图315

需要创建配置中心的客户端模块

cloudalibaba-Nacos-config-client-3377

1,pom

2,配置文件

这里需要配置两个配置文件,application.ymk和bootstarp.yml

  1. 主要是为了可以与spring clodu config无缝迁移

SpringCloud - 图316

  1. 可以看到

SpringCloud - 图317

3,主启动类

SpringCloud - 图318

4,controller

SpringCloud - 图319

  1. 可以看到,这里也添加了@RefreshScope
  2. 之前在Config配置中心,也是添加这个注解实现动态刷新的

SpringCloud - 图320

5,在Nacos添加配置信息:

Nacos的配置规则:

SpringCloud - 图321

配置规则,就是我们在客户端如何指定读取配置文件,配置文件的命名的规则

默认的命名方式:

SpringCloud - 图322

  1. prefix:
  2. 默认就是当前服务的服务名称
  3. 也可以通过spring.cloud.necos.config.prefix配置
  4. spring.profile.active:
  5. 就是我们在application.yml中指定的,当前是开发环境还是测试等环境
  6. 这个可以不配置,如果不配置,那么前面的 - 也会没有
  7. file-extension
  8. 就是当前文件的格式(后缀),目前只支持ymlproperties

SpringCloud - 图323

SpringCloud - 图324

在web UI上创建配置文件:

SpringCloud - 图325

SpringCloud - 图326

注意,DataId就是配置文件名字:

  1. 名字一定要按照上面的规则命名,否则客户端会读取不到配置文件

6,测试

重启3377客户端

访问3377

SpringCloud - 图327

拿到了配置文件中的值

7,注意默认就开启了自动刷新

此时我们修改了配置文件

客户端是可以立即更新的

  1. 因为Nacos支持Bus总线,会自动发送命令更新所有客户端

Nacos配置中心之分类配置:

SpringCloud - 图328

SpringCloud - 图329

SpringCloud - 图330

NameSpace默认有一个:public名称空间

这三个类似java的: 包名 + 类名 + 方法名

SpringCloud - 图331

SpringCloud - 图332

1,配置不同DataId:

SpringCloud - 图333

SpringCloud - 图334

  1. 通过配置文件,实现多环境的读取:

SpringCloud - 图335

  1. 此时,改为dev,就会读取dev的配置文件,改为test,就会读取test的配置文件

2,配置不同的GroupID:

直接在新建配置文件时指定组

SpringCloud - 图336

SpringCloud - 图337

在客户端配置,使用指定组的配置文件:

SpringCloud - 图338

这两个配置文件都要修改

SpringCloud - 图339

重启服务,即可

配置不同的namespace:

SpringCloud - 图340

SpringCloud - 图341

客户端配置使用不同名称空间:

SpringCloud - 图342

要通过命名空间id指定

OK,测试

Nacos集群和持久化配置:

SpringCloud - 图343

Nacos默认有自带嵌入式数据库,derby,但是如果做集群模式的话,就不能使用自己的数据库

  1. 不然每个节点一个数据库,那么数据就不统一了,需要使用外部的mysql

SpringCloud - 图344

1,单机版,切换mysql数据库:

  1. **将nacos切换到使用我们自己的mysql数据库:**

1,nacos默认自带了一个sql文件,在nacos安装目录下

  1. 将它放到我们的mysql执行

2,修改Nacos安装目录下的安排application.properties,添加:

SpringCloud - 图345

3,此时可以重启nacos,那么就会改为使用我们自己的mysql

Linux上配置Nacos集群+Mysql数据库

官方架构图:

SpringCloud - 图346

需要一个Nginx作为VIP

1,下载安装Nacos的Linux版安装包

2,进入安装目录,现在执行自带的sql文件

  1. 进入mysql,执行sql文件

3.修改配置文件,切换为我们的mysql

  1. 就是上面windos版要修改的几个属性

4,修改cluster.conf,指定哪几个节点是Nacos集群

  1. 这里使用3333,4444,5555作为三个Nacos节点监听的端口

SpringCloud - 图347

5,我们这里就不配置在不同节点上了,就放在一个节点上

  1. 既然要在一个节点上启动不同Nacos实例,就要修改startup.sh,使其根据不同端口启动不同Nacos实例

SpringCloud - 图348

SpringCloud - 图349

可以看到,这个脚本就是通过jvm启动nacos

  1. 所以我们最后修改的就是,nohup java -Dserver.port=3344

6,配置Nginx:

  1. ![](.%5C%E5%9B%BE%E7%89%87%5CAlibaba%E7%9A%8450.png#alt=)

7,启动Nacos:
./startup.sh -p 3333

  1. ./startup.sh -p 4444
  2. ./startup.sh -p 5555

7,启动nginx

8,测试:

  1. 访问192.168.159.121:1111
  2. 如果可以进入nacosweb界面,就证明安装成功了

9,将微服务注册到Nacos集群:
SpringCloud - 图350

10,进入Nacos的web界面

  1. 可以看到,已经注册成功

SpringCloud - 图351

Sentinel:

实现熔断与限流,就是Hystrix

SpringCloud - 图352

  1. ![](.%5C%E5%9B%BE%E7%89%87%5CAlibaba%E7%9A%8454.png#alt=)

使用sentinel:

1,下载sentinel的jar包

2,运行sentinel

  1. 由于是一个jar包,所以可以直接java -jar运行
  2. 注意,默认sentinel占用8080端口

3,访问sentinel

  1. localhost:8080

微服务整合sentinel:

1,启动Nacos

2,新建一个项目,8401,主要用于配置sentinel
  1. pom
  2. 配置文件
  1. ![](.\图\Alibaba55.png)
  1. 主启动类
  1. ![](.\图\Alibaba56.png)
  1. controller\
  1. ![](.\图\sentinel1.png)
  1. 到这里就可以启动8401
  1. 此时我们到sentinel中查看,发现并8401的任何信息
  2. 是因为,sentinel是懒加载,需要我们执行一次访问,才会有信息
  3. 访问localhost/8401/testA
  4. ![](.\图\sentinel2.png)
  1. 可以看到.已经开始监听了

sentinel的流控规则

流量限制控制规则

SpringCloud - 图353

SpringCloud - 图354

SpringCloud - 图355

流控模式:

  1. 直接快速失败
  1. ![](.\图\sentinel9.png)
  2. ![](.\图\sentinel5.png)
  3. ==直接失败的效果:==
  4. ![](.\图\sentinel6.png)
  1. 线程数:
    SpringCloud - 图356
    SpringCloud - 图357``` 比如a请求过来,处理很慢,在一直处理,此时b请求又过来了
    1. 此时因为a占用一个线程,此时要处理b请求就只有额外开启一个线程
    2. 那么就会报错
    ```
    SpringCloud - 图358
  2. 关联:
    SpringCloud - 图359
    应用场景: 比如支付接口达到阈值,就要限流下订单的接口,防止一直有订单
    SpringCloud - 图360
    当testA达到阈值,qps大于1,就让testB之后的请求直接失败
    可以使用postman压测
  1. 链路:
    多个请求调用同一个微服务
  2. 预热Warm up:
  1. ![](.\图\sentinel14.png)
  2. ![](.\图\sentinel15.png)
  3. ![](.\图\sentinel16.png)
  4. ==应用场景==
  5. ![](.\图\sentinel17.png)
  1. 排队等待:
  1. ![](.\图\sentinel18.png)
  2. ![](.\图\sentinel19.png)

降级规则:

就是熔断降级

SpringCloud - 图361

SpringCloud - 图362

SpringCloud - 图363

SpringCloud - 图364

1,RT配置:

新增一个请求方法用于测试

SpringCloud - 图365

配置RT:

  1. 这里配置的PT,默认是秒级的平均响应时间

SpringCloud - 图366

默认计算平均时间是: 1秒类进入5个请求,并且响应的平均值超过阈值(这里的200ms),就报错]

  1. 15请求是Sentinel默认设置的

测试

SpringCloud - 图367

SpringCloud - 图368

默认熔断后.就直接抛出异常

2,异常比例:

SpringCloud - 图369

修改请求方法

SpringCloud - 图370

配置:

SpringCloud - 图371

如果没触发熔断,这正常抛出异常:

SpringCloud - 图372

触发熔断:

SpringCloud - 图373

3, 异常数:

SpringCloud - 图374

SpringCloud - 图375

一分钟之内,有5个请求发送异常,进入熔断

热点规则:

SpringCloud - 图376

  1. ![](.%5C%E5%9B%BE%E7%89%87%5Csentinel%E7%9A%8437.png#alt=)

比如:

  1. localhost:8080/aa?name=aa
  2. localhost:8080/aa?name=b'b
  3. 加入两个请求中,带有参数aa的请求访问频次非常高,我们就现在name==aa的请求,但是bb的不限制

如何自定义降级方法,而不是默认的抛出异常?

SpringCloud - 图377

使用@SentinelResource直接实现降级方法,它等同Hystrix的@HystrixCommand

SpringCloud - 图378

定义热点规则:

SpringCloud - 图379

SpringCloud - 图380

此时我们访问/testHotkey并且带上才是p1

  1. 如果qps大于1,就会触发我们定义的降级方法

SpringCloud - 图381

但是我们的参数是P2,就没有问题

SpringCloud - 图382

只有带了p1,才可能会触发热点限流

SpringCloud - 图383

2,设置热点规则中的其他选项:

SpringCloud - 图384

需求:

SpringCloud - 图385

SpringCloud - 图386

测试

SpringCloud - 图387

SpringCloud - 图388

注意:

参数类型只支持,8种基本类型+String类

注意:

如果我们程序出现异常,是不会走blockHander的降级方法的,因为这个方法只配置了热点规则,没有配置限流规则

我们这里配置的降级方法是sentinel针对热点规则配置的

只有触发热点规则才会降级

SpringCloud - 图389

3,系统规则:

系统自适应限流:
从整体维度对应用入口进行限流

对整体限流,比如设置qps到达100,这里限流会限制整个系统不可以

SpringCloud - 图390

SpringCloud - 图391

测试:
SpringCloud - 图392

SpringCloud - 图393

@SentinelResource注解:

用于配置降级等功能

1,环境搭建

  1. 为8401添加依赖
    添加我们自己的commone包的依赖
    SpringCloud - 图394
  2. 额外创建一个controller类
  1. ![](.\图\sentinel56.png)
  1. 配置限流
  1. **注意,我们这里配置规则,资源名指定的是@SentinelResource注解value的值,**
  2. **这样也是可以的,也就是不一定要指定访问路径**
  3. ![](.\图\sentinel57.png)
  1. 测试.
  1. 可以看到已经进入降级方法了
  2. ![](.\图\sentinel58.png)
  1. 此时我们关闭8401服务
  1. 可以看到,这些定义的规则是临时的,关闭服务,规则就没有了
  2. ![](.\图\sentinel59.png)

可以看到,上面配置的降级方法,又出现Hystrix遇到的问题了

  1. 降级方法与业务方法耦合
  2. 每个业务方法都需要对应一个降级方法

自定义限流处理逻辑:

  1. 单独创建一个类,用于处理限流
    SpringCloud - 图395
  2. 在controller中,指定使用自定义类中的方法作为降级方法
    SpringCloud - 图396
  3. Sentinel中定义流控规则:
    这里资源名,是以url指定,也可以使用@SentinelResource注解value的值指定
    SpringCloud - 图397
  4. 测试:
    SpringCloud - 图398
  5. 整体:
    SpringCloud - 图399

@SentinelResource注解的其他属性:

SpringCloud - 图400

SpringCloud - 图401

服务熔断:

  1. 启动nacos和sentinel
  2. 新建两个pay模块 9003和9004
  3. pom
  4. 配置文件
  1. ![](.\图\sentinel的的8.png)*
  2. 3. 主启动类
  3. ```java
  4. @SpringBootApplication
  5. @EnableDiscoveryClient
  6. public class PaymentMain9003 {
  7. public static void main(String[] args) {
  8. SpringApplication.run(PaymentMain9003.class,args);
  9. }
  10. }
  1. controller

    SpringCloud - 图402

    然后启动9003.9004 ```

  2. 新建一个order-84消费者模块:

  3. pom
  1. 与上面的pay一模一样
  2. 2. 配置文件
  3. ![](.\图\sentinel的的10.png)
  4. 3. 主启动类
  5. ![](.\图\sentinel的的11.png)
  6. 4. 配置类
  7. ![](.\图\sentinel的的12.png)
  8. 5. controller
  9. ![](.\图\sentinel的的13.png)
  10. 6. **==为业务方法添加fallback来指定降级方法==**:
  11. ![](.\图\sentinel的的14.png)
  12. ==重启order==
  13. 测试:
  14. ![](.\图\sentinel的的15.png)
  15. ==所以,fallback是用于管理异常的,当业务方法发生异常,可以降级到指定方法==
  16. 注意,我们这里==并没有使用sentinel配置任何规则==,但是却降级成功,就是因为
  17. fallback是用于管理异常的,当业务方法发生异常,可以降级到指定方法==
  18. 7. **==为业务方法添加blockHandler,看看是什么效果==**
  19. ![](.\图\sentinel的的16.png)
  20. **重启84,访问业务方法:**
  21. ![](.\图\sentinel的的17.png)
  22. 可以看到.,直接报错了,并没有降级
  23. 也就是说,blockHandler==只对sentienl定义的规则降级==
  24. 8. **==如果fallbackblockHandler都配置呢?==**]
  25. ![](.\图\sentinel的的18.png)
  26. **设置qps规则,阈值1**
  27. ![](.\图\sentinel的的19.png)
  28. ==测试:==
  29. ![](.\图\sentinel的的20.png)
  30. 可以看到,当两个都同时生效时,==blockhandler优先生效==
  31. 9. **==@SentinelResource还有一个属性,exceptionsToIgnore==**
  32. ![](.\图\sentinel的的21.png)
  33. **exceptionsToIgnore指定一个异常类,**
  34. **表示如果当前方法抛出的是指定的异常,不降级,直接对用户抛出异常**
  35. ![](.\图\sentinel的的22.png)

sentinel整合ribbon+openFeign+fallback

  1. 修改84模块,使其支持feign

    1. pom
      SpringCloud - 图403
    2. 配置文件
      SpringCloud - 图404
    3. 主启动类,也要修改
      SpringCloud - 图405
    4. 创建远程调用pay模块的接口
      SpringCloud - 图406
    5. 创建这个接口的实现类,用于降级
      SpringCloud - 图407
    6. 再次修改接口,指定降级类

      1. ![](.\图\sentinel的的28.png)
    7. controller添加远程调用

      1. ![](.\图\sentinel的的29.png)
    8. 测试
      启动9003,84

    9. 测试,如果关闭9003.看看84会不会降级 ``` SpringCloud - 图408

可以看到,正常降级了

  1. **熔断框架比较**
  2. ![](.%5C%E5%9B%BE%E7%89%87%5Csentinel%E7%9A%84%E7%9A%8431.png#alt=)
  3. <a name="dff5ec4e"></a>
  4. ### sentinel持久化规则
  5. 默认规则是临时存储的,重启sentinel就会消失
  6. ![](.%5C%E5%9B%BE%E7%89%87%5Csentinel%E7%9A%84%E7%9A%8432.png#alt=)
  7. **这里以之前的8401为案例进行修改:**
  8. 1. 修改8401pom```xml
  9. 添加:
  10. <!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
  11. <dependency>
  12. <groupId>com.alibaba.csp</groupId>
  13. <artifactId>sentinel-datasource-nacos</artifactId>
  14. </dependency>
  1. 修改配置文件:
  1. 添加:
  2. ![](.\图\sentinel的的33.png)
  3. **实际上就是指定,我们的规则要保证在哪个名称空间的哪个分组下**
  4. 这里没有指定namespace, 但是是可以指定的
  5. **注意,这里的dataid要与8401的服务名一致**
  1. 在nacos中创建一个配置文件,dataId就是上面配置文件中指定的
    SpringCloud - 图409
    json中,这些属性的含义:
  1. ![](.\图\sentinel的的35.png)
  1. 启动8401:
    SpringCloud - 图410
    可以看到,直接读取到了规则
  2. 关闭8401
  1. ![](.\图\sentinel的的37.png)
  1. 此时重启8401,如果sentinel又可以正常读取到规则,那么证明持久化成功
  1. 可以看到,又重新出现了
  2. ![](.\图\sentinel的的38.png)

Seata:

是一个分布式事务的解决方案,

分布式事务中的一些概念,也是seata中的概念:

  1. ![](.%5C%E5%9B%BE%E7%89%87%5Cseala.png#alt=)

SpringCloud - 图411

SpringCloud - 图412

seata安装:

  1. 下载安装seata的安装包
  2. 修改file.conf
    SpringCloud - 图413
    SpringCloud - 图414
    SpringCloud - 图415
  3. mysql建库建表
    1,上面指定了数据库为seata,所以创建一个数据库名为seata
    2,建表,在seata的安装目录下有一个db_store.sql,运行即可
  4. 继续修改配置文件,修改registry.conf
  1. 配置seata作为微服务,指定注册中心
  2. ![](.\图\seala7.png)
  1. 启动
  1. 先启动nacos
  2. 在启动seata-server(运行安装目录下的,seata-server.bat)

业务说明

SpringCloud - 图416

下单—->库存—->账号余额

  1. 创建三个数据库
    SpringCloud - 图417
  2. 创建对应的表
  1. ![](.\图\seala10.png)
  1. 创建回滚日志表,方便查看
  1. ![](.\图\seala11.png)
  2. **注意==每个库都要执行一次==这个sql,生成回滚日志表**
  1. 每个业务都创建一个微服务,也就是要有三个微服务,订单,库存,账号
  1. ==订单==,seta-order-2001
  2. 1. pom
  3. 2. 配置文件
  4. ```yaml
  5. server:
  6. port: 2001
  7. spring:
  8. application:
  9. name: seata-order-service
  10. cloud:
  11. alibaba:
  12. seata:
  13. # 自定义事务组名称需要与seata-server中的对应,我们之前在seata的配置文件中配置的名字
  14. tx-service-group: fsp_tx_group
  15. nacos:
  16. discovery:
  17. server-addr: 127.0.0.1:8848
  18. datasource:
  19. # 当前数据源操作类型
  20. type: com.alibaba.druid.pool.DruidDataSource
  21. # mysql驱动类
  22. driver-class-name: com.mysql.cj.jdbc.Driver
  23. url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
  24. username: root
  25. password: root
  26. feign:
  27. hystrix:
  28. enabled: false
  29. logging:
  30. level:
  31. io:
  32. seata: info
  33. mybatis:
  34. mapperLocations: classpath*:mapper/*.xml
  1. 还要额外创建其他配置文件,创建一个file.conf:
  2. ```.conf
  3. transport {
  4. # tcp udt unix-domain-socket
  5. type = "TCP"
  6. #NIO NATIVE
  7. server = "NIO"
  8. #enable heartbeat
  9. heartbeat = true
  10. #thread factory for netty
  11. thread-factory {
  12. boss-thread-prefix = "NettyBoss"
  13. worker-thread-prefix = "NettyServerNIOWorker"
  14. server-executor-thread-prefix = "NettyServerBizHandler"
  15. share-boss-worker = false
  16. client-selector-thread-prefix = "NettyClientSelector"
  17. client-selector-thread-size = 1
  18. client-worker-thread-prefix = "NettyClientWorkerThread"
  19. # netty boss thread size,will not be used for UDT
  20. boss-thread-size = 1
  21. #auto default pin or 8
  22. worker-thread-size = 8
  23. }
  24. shutdown {
  25. # when destroy server, wait seconds
  26. wait = 3
  27. }
  28. serialization = "seata"
  29. compressor = "none"
  30. }
  31. service {
  32. #vgroup->rgroup
  33. # 事务组名称
  34. vgroup_mapping.fsp_tx_group = "default"
  35. #only support single node
  36. default.grouplist = "127.0.0.1:8091"
  37. #degrade current not support
  38. enableDegrade = false
  39. #disable
  40. disable = false
  41. #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  42. max.commit.retry.timeout = "-1"
  43. max.rollback.retry.timeout = "-1"
  44. }
  45. client {
  46. async.commit.buffer.limit = 10000
  47. lock {
  48. retry.internal = 10
  49. retry.times = 30
  50. }
  51. report.retry.count = 5
  52. tm.commit.retry.count = 1
  53. tm.rollback.retry.count = 1
  54. }
  55. ## transaction log store
  56. store {
  57. ## store mode: file、db
  58. #mode = "file"
  59. mode = "db"
  60. ## file store
  61. file {
  62. dir = "sessionStore"
  63. # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
  64. max-branch-session-size = 16384
  65. # globe session size , if exceeded throws exceptions
  66. max-global-session-size = 512
  67. # file buffer size , if exceeded allocate new buffer
  68. file-write-buffer-cache-size = 16384
  69. # when recover batch read size
  70. session.reload.read_size = 100
  71. # async, sync
  72. flush-disk-mode = async
  73. }
  74. ## database store
  75. db {
  76. ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
  77. datasource = "dbcp"
  78. ## mysql/oracle/h2/oceanbase etc.
  79. db-type = "mysql"
  80. driver-class-name = "com.mysql.jdbc.Driver"
  81. url = "jdbc:mysql://127.0.0.1:3306/seata"
  82. user = "root"
  83. password = "root"
  84. min-conn = 1
  85. max-conn = 3
  86. global.table = "global_table"
  87. branch.table = "branch_table"
  88. lock-table = "lock_table"
  89. query-limit = 100
  90. }
  91. }
  92. lock {
  93. ## the lock store mode: local、remote
  94. mode = "remote"
  95. local {
  96. ## store locks in user's database
  97. }
  98. remote {
  99. ## store locks in the seata's server
  100. }
  101. }
  102. recovery {
  103. #schedule committing retry period in milliseconds
  104. committing-retry-period = 1000
  105. #schedule asyn committing retry period in milliseconds
  106. asyn-committing-retry-period = 1000
  107. #schedule rollbacking retry period in milliseconds
  108. rollbacking-retry-period = 1000
  109. #schedule timeout retry period in milliseconds
  110. timeout-retry-period = 1000
  111. }
  112. transaction {
  113. undo.data.validation = true
  114. undo.log.serialization = "jackson"
  115. undo.log.save.days = 7
  116. #schedule delete expired undo_log in milliseconds
  117. undo.log.delete.period = 86400000
  118. undo.log.table = "undo_log"
  119. }
  120. ## metrics settings
  121. metrics {
  122. enabled = false
  123. registry-type = "compact"
  124. # multi exporters use comma divided
  125. exporter-list = "prometheus"
  126. exporter-prometheus-port = 9898
  127. }
  128. support {
  129. ## spring
  130. spring {
  131. # auto proxy the DataSource bean
  132. datasource.autoproxy = false
  133. }
  134. }
  135. ```
  136. 创建registry.conf:
  137. ```conf
  138. registry {
  139. # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  140. type = "nacos"
  141. nacos {
  142. #serverAddr = "localhost"
  143. serverAddr = "localhost:8848"
  144. namespace = ""
  145. cluster = "default"
  146. }
  147. eureka {
  148. serviceUrl = "http://localhost:8761/eureka"
  149. application = "default"
  150. weight = "1"
  151. }
  152. redis {
  153. serverAddr = "localhost:6379"
  154. db = "0"
  155. }
  156. zk {
  157. cluster = "default"
  158. serverAddr = "127.0.0.1:2181"
  159. session.timeout = 6000
  160. connect.timeout = 2000
  161. }
  162. consul {
  163. cluster = "default"
  164. serverAddr = "127.0.0.1:8500"
  165. }
  166. etcd3 {
  167. cluster = "default"
  168. serverAddr = "http://localhost:2379"
  169. }
  170. sofa {
  171. serverAddr = "127.0.0.1:9603"
  172. application = "default"
  173. region = "DEFAULT_ZONE"
  174. datacenter = "DefaultDataCenter"
  175. cluster = "default"
  176. group = "SEATA_GROUP"
  177. addressWaitTime = "3000"
  178. }
  179. file {
  180. name = "file.conf"
  181. }
  182. }
  183. config {
  184. # file、nacos 、apollo、zk、consul、etcd3
  185. type = "file"
  186. nacos {
  187. serverAddr = "localhost"
  188. namespace = ""
  189. }
  190. consul {
  191. serverAddr = "127.0.0.1:8500"
  192. }
  193. apollo {
  194. app.id = "seata-server"
  195. apollo.meta = "http://192.168.1.204:8801"
  196. }
  197. zk {
  198. serverAddr = "127.0.0.1:2181"
  199. session.timeout = 6000
  200. connect.timeout = 2000
  201. }
  202. etcd3 {
  203. serverAddr = "http://localhost:2379"
  204. }
  205. file {
  206. name = "file.conf"
  207. }
  208. }
  209. ```
  210. ==实际上,就是要将seata中的我们之前修改的两个配置文件复制到这个项目下==
  1. 主启动类

    1. @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) //取消数据源的自动创建
    2. @EnableDiscoveryClient
    3. @EnableFeignClients
    4. public class SeataOrderMain2001 {
    5. public static void main(String[] args) {
    6. SpringApplication.run(SeataOrderMain2001.class,args);
    7. }
    8. }
  1. service层

    1. public interface OrderService {
    2. /**
    3. * 创建订单
    4. * @param order
    5. */
    6. void create(Order order);
    7. }
    1. @FeignClient(value = "seata-storage-service")
    2. public interface StorageService {
    3. /**
    4. * 减库存
    5. * @param productId
    6. * @param count
    7. * @return
    8. */
    9. @PostMapping(value = "/storage/decrease")
    10. CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
    11. }

    ```xml @FeignClient(value = “seata-account-service”) public interface AccountService {

    /**

    1. * 减余额
    2. * @param userId
    3. * @param money
    4. * @return
    5. */

    @PostMapping(value = “/account/decrease”) CommonResult decrease(@RequestParam(“userId”) Long userId, @RequestParam(“money”) BigDecimal money); }

  1. ```
  2. ```xml
  3. @Service
  4. @Slf4j
  5. public class OrderServiceImpl implements OrderService {
  6. @Resource
  7. private OrderDao orderDao;
  8. @Resource
  9. private AccountService accountService;
  10. @Resource
  11. private StorageService storageService;
  12. /**
  13. * 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
  14. * 简单说:
  15. * 下订单->减库存->减余额->改状态
  16. * GlobalTransactional seata开启分布式事务,异常时回滚,name保证唯一即可
  17. * @param order 订单对象
  18. */
  19. @Override
  20. ///@GlobalTransactional(name = "fsp-create-order", rollbackFor = Exception.class)
  21. public void create(Order order) {
  22. // 1 新建订单
  23. log.info("----->开始新建订单");
  24. orderDao.create(order);
  25. // 2 扣减库存
  26. log.info("----->订单微服务开始调用库存,做扣减Count");
  27. storageService.decrease(order.getProductId(), order.getCount());
  28. log.info("----->订单微服务开始调用库存,做扣减End");
  29. // 3 扣减账户
  30. log.info("----->订单微服务开始调用账户,做扣减Money");
  31. accountService.decrease(order.getUserId(), order.getMoney());
  32. log.info("----->订单微服务开始调用账户,做扣减End");
  33. // 4 修改订单状态,从0到1,1代表已完成
  34. log.info("----->修改订单状态开始");
  35. orderDao.update(order.getUserId(), 0);
  36. log.info("----->下订单结束了,O(∩_∩)O哈哈~");
  37. }
  38. }
  39. ```
  1. dao层,也就是接口

    1. @Mapper
    2. public interface OrderDao {
    3. /**
    4. * 1 新建订单
    5. * @param order
    6. * @return
    7. */
    8. int create(Order order);
    9. /**
    10. * 2 修改订单状态,从0改为1
    11. * @param userId
    12. * @param status
    13. * @return
    14. */
    15. int update(@Param("userId") Long userId, @Param("status") Integer status);
    16. }

    ==在resource下创建mapper文件夹,编写mapper.xml==

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    3. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    4. <mapper namespace="com.eiletxie.springcloud.alibaba.dao.OrderDao">
    5. <resultMap id="BaseResultMap" type="com.eiletxie.springcloud.alibaba.domain.Order">
    6. <id column="id" property="id" jdbcType="BIGINT"></id>
    7. <result column="user_id" property="userId" jdbcType="BIGINT"></result>
    8. <result column="product_id" property="productId" jdbcType="BIGINT"></result>
    9. <result column="count" property="count" jdbcType="INTEGER"></result>
    10. <result column="money" property="money" jdbcType="DECIMAL"></result>
    11. <result column="status" property="status" jdbcType="INTEGER"></result>
    12. </resultMap>
    13. <insert id="create" parameterType="com.eiletxie.springcloud.alibaba.domain.Order" useGeneratedKeys="true"
    14. keyProperty="id">
    15. insert into t_order(user_id,product_id,count,money,status) values (#{userId},#{productId},#{count},#{money},0);
    16. </insert>
    17. <update id="update">
    18. update t_order set status =1 where user_id =#{userId} and status=#{status};
    19. </update>
    20. </mapper>
  2. controller层

    ```java @RestController public class OrderController { @Resource private OrderService orderService;

  1. /**
  2. * 创建订单
  3. *
  4. * @param order
  5. * @return
  6. */
  7. @GetMapping("/order/create")
  8. public CommonResult create(Order order) {
  9. orderService.create(order);
  10. return new CommonResult(200, "订单创建成功");
  11. }
  12. }
  13. ```
  1. entity类(也叫domain类)

    1. @Data
    2. @AllArgsConstructor
    3. @NoArgsConstructor
    4. public class CommonResult<T> {
    5. private Integer code;
    6. private String message;
    7. private T data;
    8. public CommonResult(Integer code, String message) {
    9. this(code, message, null);
    10. }
    11. }

    SpringCloud - 图418

  1. config配置类

    ```java @Configuration @MapperScan({“com.eiletxie.springcloud.alibaba.dao”}) 指定我们的接口的位置 public class MyBatisConfig {

  1. }
  2. ```
  3. ```java
  4. /**
  5. * @Author EiletXie
  6. * @Since 2020/3/18 21:51
  7. * 使用Seata对数据源进行代理
  8. */
  9. @Configuration
  10. public class DataSourceProxyConfig {
  11. @Value("${mybatis.mapperLocations}")
  12. private String mapperLocations;
  13. @Bean
  14. @ConfigurationProperties(prefix = "spring.datasource")
  15. public DataSource druidDataSource() {
  16. return new DruidDataSource();
  17. }
  18. @Bean
  19. public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
  20. return new DataSourceProxy(druidDataSource);
  21. }
  22. @Bean
  23. public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
  24. SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
  25. bean.setDataSource(dataSourceProxy);
  26. ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
  27. bean.setMapperLocations(resolver.getResources(mapperLocations));
  28. return bean.getObject();
  29. }
  30. }
  31. ```
  1. ==库存==,seta-storage-2002

==看脑图==

  1. pom
  2. 配置文件
  3. 主启动类
  4. service层
  5. dao层
  6. controller层

==账号==,seta-account-2003

==看脑图==

  1. pom
  2. 配置文件
  3. 主启动类
  4. service层
  5. dao层
  6. controller层
  7. 8. ```

  8. 全局创建完成后,首先测试不加seata
    SpringCloud - 图419
    SpringCloud - 图420

  1. 使用seata:
    在订单模块的serviceImpl类中的create方法添加启动分布式事务的注解
  1. /**
  2. 这里添加开启分布式事务的注解,name指定当前全局事务的名称
  3. rollbackFor表示,发生什么异常需要回滚
  4. noRollbackFor:表示,发生什么异常不需要回滚
  5. */
  6. @GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)
  7. ///@GlobalTransactional(name = "fsp-create-order", rollbackFor = Exception.class)
  8. public void create(Order order) {
  9. // 1 新建订单
  10. log.info("----->开始新建订单");
  11. orderDao.create(order);
  12. // 2 扣减库存
  13. log.info("----->订单微服务开始调用库存,做扣减Count");
  14. storageService.decrease(order.getProductId(), order.getCount());
  15. log.info("----->订单微服务开始调用库存,做扣减End");
  16. // 3 扣减账户
  17. log.info("----->订单微服务开始调用账户,做扣减Money");
  18. accountService.decrease(order.getUserId(), order.getMoney());
  19. log.info("----->订单微服务开始调用账户,做扣减End");
  20. // 4 修改订单状态,从0到1,1代表已完成
  21. log.info("----->修改订单状态开始");
  22. orderDao.update(order.getUserId(), 0);
  23. log.info("----->下订单结束了,O(∩_∩)O哈哈~");
  24. }
  1. 7. 此时在测试

发现,发生异常后,直接回滚了,前面的修改操作都回滚了 ```

setat原理:

SpringCloud - 图421

SpringCloud - 图422

seata提供了四个模式:

SpringCloud - 图423

SpringCloud - 图424

第一阶段:

SpringCloud - 图425

  1. ![](.%5C%E5%9B%BE%E7%89%87%5Cseala%E7%9A%8419.png#alt=)

二阶段之提交:

SpringCloud - 图426

二阶段之回滚:

SpringCloud - 图427

SpringCloud - 图428

断点:

SpringCloud - 图429

可以看到,他们的xid全局事务id是一样的,证明他们在一个事务下

SpringCloud - 图430

before 和 after的原理就是

SpringCloud - 图431

在更新数据之前,先解析这个更新sql,然后查询要更新的数据,进行保存