黑马SpringCloud微服务课程总结(一)
参考课程:SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式,系统详解springcloud微服务技术栈课程|黑马程序员Java微服务_哔哩哔哩_bilibili
总结内容包括:Ribbon注册中心,Nacos注册中心,Feign远程调用,GateWay网关,微服务保护Sentinel
Ribbon
负载均衡原理
所以中间肯定有个东西把请求拦截下来然后做处理了
我们的浏览器和我们的服务器中间,肯定会有一些东西把我们的请求拦截下来做处理
轮询机制
创建RedisTemplate并且注入SpringIOC容器
Bean注解,注入IOC容器
LoadBalanced注解,说明我们的这个RestTemplate请求将要被我们的ribbon拦截处理
看看底层,其实我们是指定了规则的,例如我们的轮询规则
rule.choose我们是按照规则来选的
IRule类
负载均衡策略
我们的规则是由一个IRule的接口来定义的,每一个子接口都是一种规则
例如轮询
两种方式修改我们的负载均衡规则
new一个Irule
在我们的启动类里面,新创建一个IRule类,然后把它注册成bean
编写我们的application文件
在yml文件中,服务名:ribbon:NFLoadBalancerRuleClassName :规则
区别
第一种方式是针对全体
第二种方式是对某个服务而言
饥饿加载
默认是懒加载,饥饿加载默认是项目启动时候创建,会降低第一次访问的耗时
配置文件指定鸡儿加载的服务名称
Nacos
快速入门
Nacos是我们的服务注册中心,默认账号密码是nacos,nacos
引入相关依赖
引入我们的Nacos管理依赖
<!--nacos的管理依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2022.0.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
添加Nacos客户端依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
springboot3版本及以上,还要加LoadBalancer依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>3.0.2</version>
</dependency>
我们一般在bootstartp.yml文件里面来编写我们的nacos和springcloud相关的配置,所以我们引入这个依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.0.2</version>
</dependency>
yml文件配置
配置我们的Nacos注册中心的地址,默认端口是8848
小结
分级存储模型
分为服务,集群和实例
尽可能使用本地集群服务 ,因为跨集群调用延迟会很高
默认集群名称
点进去发现我们的集群名称是default
修改配置文件
cluster-name
配置当前服务的集群的名称
我们用Control+单机来一次性启动多个服务
三级存储
服务
集群
实例
mybatis配置
type-aliases-package: cn.itcast.order.pojo
这个配置指定了 MyBatis 在搜索类型别名时需要扫描的包名。
也就是说,MyBatis 会自动扫描 cn.itcast.order.pojo 包及其子包下的所有类,并为它们定义默认的类型别名。
这样在 MyBatis 的 XML 映射文件中,就可以直接使用类的简单名称,而不需要写出完整的类名。
configuration: map-underscore-to-camel-case: true
这个配置开启了 MyBatis 的自动驼峰命名规则转换功能。
也就是说,如果数据库表的字段名使用了下划线命名法,那么 MyBatis 会自动将查询结果的字段名转换为驼峰命名法对应的属性名。
比如数据库表字段 order_id 会自动转换为 Java 对象的属性 orderId。
这样可以减少手动配置字段名与属性名的映射关系,提高开发效率。
Nacosrule负载均衡
我们不想要轮询,例如我们的轮询是8081,8082,8083
我们就只想让,8081,8082,然后回到8081这种
所以我们要修改我们的负载均衡
这个规则会优先寻找和自己同集群的服务
本地集群没有服务的时候也可以访问
警告信息,一次跨集群访问发生了
优先访问本地,本地没才会跨集群访问
如果跨集群访问的话,就会出现这种警告
设置权重值
可以设置我们的权重值
我们根据权重大小来分配我们的用户请求
NameSpcae做环境隔离
我们没设置命名空间的情况下,我们默认都在我们的public空间
修改命名空间后配置yml文件
如果要修改我们的命名空间,我们要去我们的代码区域修改
配置下面,添加多一个namespace
namespace用来做环境隔壁
每个namespace都有唯一ID
不同的namespace下服务不可见
配置管理
统一配置管理
实现配置管理热更新
DataId必须唯一且不能冲突
我们尽量只把有热更新需求的配置拿过来
微服务配置拉取
优先级
bootstrap的优先级>application.yml的优先级
之前的discover是我们的服务端依赖,discover是我们的注册发现
引入配置管理客户端依赖
<!--nacos配置-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
springCloud2020默认禁用了bootstrap
因为我们要使用bootstrap.yml文件,所以我们要引入相应的依赖
引入BootStrap依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.0.2</version>
</dependency>
file-extension文件后缀名
成功读取到我们的配置文件了
使用Value注解读取配置
配置热更新
我们改完配置之后,却没有立刻发生变化
方式一
添加RefreshScope注解
方式二
使用ConfigurationProperties注解
写一个实体类注册到IOC容器
我们要加个Component,让我们的这个类可以作为我们的bean去使用
Autowired刚刚写的实体类
然后使用我们刚刚写的那个类,注入进来,然后手动写入
确实做到了热更新
多环境配置共享
在Nacos中,无论如何,这个文件一定会加载,所以我们的多环境共享配置可以写入这个文件
例如其他的dev和test我们不理,但是我们的没有后缀的userservice.yml文件是一定会被读取进去的
服务名.yaml是默认服务配置 服务名-环境.yml是环境配置 环境配置优先级高于默认服务配置
我们配置的是的dev环境
但它可以同时读取userservice-dev和userservice
配置优先级
服务名-profile.yaml >服务名称.yaml >本地配置
Nacos集群搭建
我们先按照Nacos文档来,引入对应版本mysql数据库需要的表
配置集群信息
修改集群文件,cluster.conf文件,把我们需要配置的集群信息写进去
配置我们的mysql信息
开启数据源
首先我们要把我们的数据源给打开
这样子我们就告诉nacos我们用的是mysql集群而不是其他集群
数据库的数量
也就是我们的这个集群中有几个mysql
数据库具体信息
mysql的url
和我们mysql的用户密码信息了
搭建集群
我们还要把nacos复制成三份然后分别启动
修改集群端口
我们在conf里面的application.properties文件来修改
然后我们分别把这三个端口改成8845,8846,8847、
集群启动
3台都以集群的方式启动成功了
接下来就是给他们三个做好负载均衡反向代理
Nginx负载均衡反向代理
nginx -t
查看nginx的错误‘
我们在这打开我们的nginx.conf来打开我们的配置
配置nacos集群的ip和端口
我们先配置集群里面的3个ip地址和端口号
upstream
我们要为这三个ip和端口进行负载均衡反向代理
server
配置我们Nginx默认访问的时候的IP和监听端口
location是配置我们访问的默认路径
这个/nacos是我们配置的默认路径
也就是我们访问localhost:80/nacos这个路径,我们就会代理到我们上面配置的集群里面
那个proxy_pass,配置的就是我们要转发的网址
因为是基于http协议,所以我们把这个粘贴到我们的http内部,只要在http内部那我们不管粘贴到哪里都可以
然后我们打开我们的nginx
start nginx.exe
我们访问成功后,我们直接localhost/nacos
这样子就可以直接访问了
看,我们访问到了
修改java代码配置
修改端口为,我们的nginx监听的端口
那我们java代码里面该怎么修改呢
我们本来是8848,但我们监听的是80,所以我们直接80就好了
Nacos2版本及以上与Nginx结合遇到的问题
首先我们要跟着我们的Nacos文档一步一步配置,例如我们的密钥那些
无public访问权限
nginx负载均衡后,特么的竟然没有public的访问权限
我们要在配置文件中关闭我们的权限认证
没错误了
(grpc通信)新增通信协议问题(相邻端口集群失效)
Authorization (nacos.io)
端口偏移量1000和1001
例如我们配置8080和8081端口
8080端口会偏移1000和1001,也就是占用了9080和9081
8081端口会偏移1000和1001,也就是占用了9081和9082
9081端口占用冲突了,所以我们会报错
【BUG】Nacos2.0报错 “Error creating bean with name ‘grpcSdkServer‘: Invocation of init method failed;”_error creating bean with name 'grpcsdkserver': inv-CSDN博客
这个是我们的nacos2版本以上才有的
此时我们无法监听到9846这个端口,因为被占用了
我们的nacos版本2以上,我们的端口会偏移1000
所以我们杀掉这个端口,再重新启动我们的nacos
我们因为是nacos2,所以我们集群配置的时候,不能配置我们的相邻的端口
Feign远程调用
远程调用
Feign替代RestTemplate
RestTemplate远程调用的弊端
Feign就是来帮我们实现http请求的
引入openfeign依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
启动类添加注解开启OpenFeign
EnableFeignClients
编写Feign客户端
写客户端做我们的接口声明
FeignClinet注解
name,是我们的服务名称
url,是我们服务的URL路径
fallbackFactory: 指定了一个回退工厂类 UserClientFallBackFactory,用于在服务调用失败时提供备用逻辑
因为我们的两个服务的端口不同,所以我的feign发起请求的时候,我们要配置我们的url路径,里面写我们的另一个服务的端口,不然就无法成功调用
报错Feign无法注册成bean对象
导入依赖后就可以了
ribbon实现负载均衡
feign内部集成了ribbon实现了我们的负载均衡
自定义配置
feign自定义配置来覆盖我们的默认配置
feign允许我们覆盖5个配置
负责把查询到的数据转成我们的json对象
转成请求体
支持哪种注解
Feign日志配置
第一种方式
default是全局生效
userservice(也就是我们的服务名称)是针对某个服务,也就是局部生效,只针对我们的userservice这个服务的请求
第二种方式
写一个配置类,注册成bean
然后如果是全局配置,就把这个类弄到我们的启动类里面
如果是局部配置,那么就把这个类配置到我们的Client客户端类
其实也就是指定,然后让spring能够扫描到我们的自定义类里面注册的bean,然后使用
这个是指定我们全局使用默认配置
扫包Client,保证能正常调用
一个是扫包
一个是指定字节码
性能优化
使用连接池
指定日志级别
为了弄我们的连接池配置
引入Feign的HttpClient依赖
<!--连接池-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
配置文件配置我们的httpClient
来配置我们的连接池,调一些参数
最佳实践分析
消费者的feign和提供者的controller定义为统一的接口
方式一继承
这
你看我们的远程调用是UserAPI
种方法对springmvc没有用
所以我们的参数声明拿不到
我们的Pathvariable我们还要多写一遍
方式二抽取为独立模块
我们把我们的FeignClient抽取成一个独立的模块
实现最佳实践
我们把那种远程调用的东西,我们独立出一个模块
然后在我们的Service里面引入我们的模块
<dependency>
<groupId>cn.itcast.demo</groupId>
<artifactId>feign--api</artifactId>
<version>1.0</version>
</dependency>
注入失败
我们无法注入
指定我们的feign
我们的的远程调用功能是独立在一个模块里面写的,所以我们扫包要扫那个模块的包
启动类注解里面,clients指定扫描包
因为我们的两个服务的端口不同,所以我的feign发起请求的时候,我们要配置我们的url路径,里面写我们的另一个服务的端口,不然就无法成功调用
GateWay网关
作用介绍
不管怎么样cloud3以上必须引入loadBalancer依赖
权限认证
负载均衡
请求限流
gateway是基于spring中的WebFlux来实现的
快速入门
网关注册到Nacos
网关自己也是一个微服务,它要把自己也注册到nacos的
引入依赖
引入gateway依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
引入nacos服务发现依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
routes路由配置
routes是我们的路由配置
多个路由区分
我们不同的routes用
-
来隔开
id
路由的唯一标识,必须唯一,其实一般写成和服务名称一样
uri
我们的路由的目标地址
默认地址是http
lb
lb是负载均衡,后面跟着我们的服务名称
predicates断言
判断是否符合路径规则
如果我们的服务路径/user开头,那么我们就会到我们的userservice服务去
路由的过程发生了什么
我们把user开头的路径带到userservice
把order开头的路径带到我们的orderservice
路由断言工厂
filter,路由过滤器,处理请求或者响应
11种工厂
我们来演示一下before的用法
认为我们的时间在2031后才符合要求
如果我们的路由不符合的话,我们就会返回404
路由过滤器filter
我们以添加请求头为例子
看下面
Truth是key,后面的就是我们的value了
(默认过滤器)全局配置请求头
我们想要全局配置这个请求头
我们有默认过滤器这个东西
我们只要在下面配置这个东西的话,那么就会对我们的所有路由都生效
defaultFilters是对所有路由都生效的过滤器
全局过滤器GlobalFilter
GlobalFilter接口
区别在于,我们的这个GlobalFilter的逻辑是需要自己写代码实现的
定义方式是现实GlocalFilter接口
exchange
exchange是请求上下文
chain
是链,负责放行
Mono
我们的返回值是Mono
这个是我们的webflux里面的一个api
放行
这个放行,其实就是找下一个过滤器然后调用里面的方法
连接GlobalFilter接口
拦截
exchange然后给我们的response来设置状态码
让过滤器生效
Componet注解
交给IOC容器管理
Order注解
里面写我们的值,值越小,过滤器优先级越高
或者连接Ordered接口
其实我们还有一种就是,我们连接Ordered类的接口,然后实现里面的方法,来规定我们的优先级
过滤器链执行顺序
有三种过滤器
默认过滤器
路由过滤器
全局过滤器
filters和default-filters
这两个过滤器本质上是一样的,只是我们的过滤范围不一样
看看底层
我们的路由过滤器和默认过滤器,都叫GatewayFilter
我们底层有个适配器
让我们的GlobalFilter,适配变成我们的gatewayFilter去使用
路由过滤器的顺序
路由过滤器的顺序是由我们的spring指定的
按照我们的声明顺序依次递增
order值一样时
我们的过滤器就有优先顺序
default>路由>GlobalFilter
CORS跨域问题
我们的网关是基于webflux去处理的,没有我们的servlet相应的api,那么我们应该怎么去处理呢
CORS里面来写我们允许谁跨域,谁做什么事情
corsConfigurations
允许域名
允许请求方式
允许请求头
允许携带Cookie
有效期
微服务保护Sentinel
初始Sentinel
雪崩问题
某个服务故障从而因前期整个链路的所有微服务不可用
四种处理方式
超时处理
舱壁模式
熔断降级
流量控制
- 常见框架
启动sentinel
访问localhost:8080 默认账号密码都是sentinel
java -Dserver.port=8090 -jar sentinel-dashboard-1.8.1.jar
我们修改java -jar启动参数,把端口修改成8090
然后我们看看我们的8090端口
默认的账号密码是
sentinel
sentinel
引入sentinel依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
配置文件配置sentinel地址
限流规则
Controller默认被监控
默认中,我们的controller的方法被监控了
刚刚我们是这个路径被监控了
使用SentinelResource注解
如果我们想我们的mapper和Service也被监控,
那我们就要使用sentinel里面提供的注解
设置规则
我们这里有一堆东西,可以设置我们的规则
流控模式
直接-流控模式
write达到阈值时,我们会对read做限流
关联资源达到阈值时候,我们会对资源进行限流
关联-流控模式
给谁限流,就要给谁添加我们的规则
链路-流控模式
只统计某个请求
这个的意思是,我们在做限流统计时,只统计从text2进入common的请求
其他请求我们不管
但链路里面只有我们的controller资源被监控了,但是我们的service资源没有被监控
没有被监控的话,那我们就不能给他配置规则
SentinelResource注解
因为我们配置了这个方法的资源名字为goods,所以我们这里写goods
这样子我们的service就变成一个可监控资源了
流控效果
快速失败
wram up(预热)
默认一开始阈值为,规定阈值/coldFactory,coldFactory默认值是3
慢慢提高我们的阈值
排队等待
排队等待,如果等待市场超过了timeout,那么我们就拒绝然后抛出异常
热点参数限流
对某个参数进行限流
热点参数默认对springmvc的资源无效
重点在配置注解
不要点这个
热点规则
要点我们的热点规则
隔离和降级
使用线程隔离
整合Feign和Sentinel
Feign开启Sentinel功能
在配置文件中修改
Feign失败后的降级逻辑
Feign整合Sentinel
定义类实现FallbackFactory
这个其实是我们远程调用失败后返回的消息
但是如果我们远程调用的类已经弄了全局异常处理器了,就没必要多写一个降级处理逻辑了
这个对应的东西,其实就是我们的远程调用的接口
这个就是我们的远程调用的歌曲
将自定义FallbackFactory注册为bean
远程调用类添加fallbackFactory
遇到的小错误
启动类上的配置注解
这样子我就成功启动了,那个Configuration的字节码要是我们的配置类的字节码
一开始这个是我一开始配置错误的字节码,我一直用了默认的,而不是我自己配置的
然后有个小细节,之前的课程我的orderservice项目重启的时候,我的sentinel都不用重新登录,这次就要重新登录,说明我之前feign的调用是错误的
线程隔离
分为信号量隔离QPS,和线程池隔离
网关其实就是高扇出
一个是流控,一个是线程池
熔断降级
这是由状态机去实现的
慢调用,异常比例,异常数
慢调用
我们故意让它休眠一下,来触发我们的慢调用
异常数
授权规则以及规则持久化
控制调用方
把我们的调用方分为白名单和黑名单
我们要的名称其实是origin
RequestOriginParser接口
我们的sentinel是通过这个接口里的parseOrigin()这个方法,来获取请求的来源的
这个方法的作用就是,从我们的request对象中解析出我们的origin的值
可惜,默认情况下这个方法返回的结果是default
如果不为空,那我们就把我们的origin作为请求头返回
如果浏览器获得的origin头和网关获取的请求头不一样,那他们的来源名称就不一样
这样子我们就可以来编写我们的规则了
区分请求是否经过网关
然后我们的网关有一个过滤器
请求头的名字是origin,然后值是gateway
这样我们从网关过来的就有请求头,然后不是从网关过来的就没有请求头
之前如果我们知道正确的url,我们甚至可以绕过网关进行访问,这样子一点都不安全
实现步骤
连接RequestOriginParser接口
然后我们把它注册成一个bean
为网关添加过滤器
看到没,我们网关配置的请求头的origin的值是gateway
总结
我们的sentinel是用这个方法来获取我们的请求来源的
你看我们的返回值是String,我们就通过这个String来判断我们的请求来源
我们默认请求头有origin这个参数
因为我们配置从网关来的,我们的origin的参数时gateway
添加授权
我们刚刚的那个方法返回的参数,就是我们的来源的名称
然后填写我们的来源名称
我们用8088端口绕过网关,发现不行
自定义异常
明明是授权异常,却返回限流异常
不管什么异常返回的都是这个,这样子就对前端不是很友好
自定义异常
BlockExceptionHandler接口
限流是429,权限是401
然后再设定响应的类型为JSON类型
然后拼装成一个JSON返回给前端
一个是请求来源的获取接口
一个是处理BlockExceptionHandler的接口
规则持久化
每次重启我们的规则就丢失了,这是因为我们的sentinel把规则保存在我们的内存里面
三种管理配置模式
依赖于Nacos实现push模式
首先,这需要修改我们的sentinel控制台的源码
跟着文档来
我们要引入sentinel监听nacos的依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
sentinel配置配置nacos地址
配置我们的nacos的地址
配置成这样子就是我们的降级
修改sentinel-dashboard源码
然后我们还要经历这么多步骤
但文件已经给我们了,所以我们直接使用就好了
刚刚我们的nacos地址是配置在我们的yml文件里面的
我们要覆盖它的话,那我们就要jar执行的时候,来指定我们的端口
我们执行那个jar文件
看看我们的sentinel
我们先清空一下缓存
刷新后
我们就有这个
但我和老师的很多配置不一样,所以这个jar包应该也不兼容,所以刷新不出来
执行错误了
一个是走到nacos加规则,一个是走到我们的原始的代码然后加规则
nacos多出来一个这个
然后我们重启,这样子我们就从nacos里面来拿我们的规则,这样子我们就不会丢失我们的规则
我们的困难在于我们的源码的改动