typora-root-url: assets
SpringCloud
1.什么是springcloud
-
springcloud是目前国内使用最广泛的微服务
-
springcloud集成了各种微服务功能组件,并基于springboot实现组件的自动装配,提供了良好的开箱体验
另外,SpringCloud底层是依赖于SpringBoot的,并且有版本的兼容关系,如下:
2.服务拆分和远程调用
2.1服务拆分原则
- 不同的微服务,开发的业务是不重复的
- 微服务的数据独立,数据库不允许相互访问
- 微服务是面向业务的,可以将自己的业务作为接口,供其他微服务调用
2.2远程调用
在一般的服务调用中是通过浏览器发送请求到对应地址调用服务,在微服务中调用其他微服务就是远程调用
2.2.1提供者与消费者
在服务调用关系中,会有两个不同的角色:
- 服务提供者:一次业务中,被其他微服务调用.(为消费者提供接口)
- 服务消费者:一次业务中,调用其他微服务的服务.(调用提供者的接口)
但这两种角色不是绝对的,一个微服务既可以是提供者,也可以是消费者
2.2.2实现远程调用
- 注册RestTemplate实例并保存到IOC容器中
- 服务消费者使用RestTemplate发送http请求调用服务提供者的服务
参考代码:
在配置类中注册RestTeamplate实例
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
使用RestTemplate发送http请求调用服务
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
//2.设置url
String url = "http://127.0.0.1:8081/user/" + order.getUserId();
//3.请求user服务器
User user = restTemplate.getForObject(url, User.class);
//4.封装用户信息
order.setUser(user);
//5.返回
return order;
}
}
3.注册中心技术
3.1为什么使用注册中心技术
假如我们的服务提供者有多个实例,当我们的消费者去调用其服务时,
- 服务消费者在发起远程调用时,如何得知服务提供者的ip地址和端口?
- 有多个服务提供者的实例的情况下,消费者如何选择?
- 服务消费者如何得知某个服务提供者实例是否健康?
这些问题就需要我们用到SpringCloud中的注册中心技术来解决
3.2Eureka注册中心
eureka就是最广为人知的注册中心技术,其结构如下:
- 服务消费者在发起远程调用时,如何得知服务提供者的ip地址和端口?
在每一个服务提供者实例启动后,会将自己的信息注册到eureka-server中,这个过程被称为服务注册.
eureka服务器保存-服务名称到服务实例地址列表的映射关系
服务消费者根据服务名称向eureka-server中拉取实例地址,这个过程被称为拉取服务
- 有多个服务提供者的实例的情况下,消费者如何选择?
服务消费者从实例列表中通过负载均衡算法拉取选中的实例地址,向该实例地址发起远程调用
- 服务消费者如何得知某个服务提供者实例是否健康?
服务提供者实例会每隔一段时间(默认30秒)向eureka-server发送请求,报告自己的状态,称为心跳.
当超过一定时间没有发送心跳,eureka会认为该实例故障,将该实例从注册中心移除
服务提供者拉取到的实例地址就不存在故障了
**一个微服务,既是一个提供者,也是一个消费者,因此eureka将服务注册何和拉取服务等功能统一封装到了eureka-client端 **
3.3实现Eureka注册中心
接下来我们实现eureak注册中心,步骤如下:
3.3.1搭建eureka-server
1.创建一个子模块
2.引入eureka-server起步依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
3.编写启动类,添加@EnableEurekaServer注解,开启eureka注册中心功能
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
4.编写配置文件
-
配置服务端口号
-
配置服务名称
-
配置eureka地址
server:
port: 10086
spring:
application:
name: eureka-server
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
eureka-server本身也会被注册中心注册
3.3.2注册服务
1.引入eureka-client起步依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.编写配置文件
spring:
application:
name: userservice
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
3.启动多个实例
3.3.3服务拉取
1.引入依赖
2.编写配置文件
注册服务和服务拉取都被封装到了eureka-client端
此处依赖和注册服务一致
3.服务拉取和负载均衡
这一步不需要我们手动去做,在注册RestTeamplate的方法上添加@LoadBalanced注解
然后修改发送请求的固定地址为服务名称即可
参考代码:
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
//2.设置url
//String url = "http://127.0.0.1:8081/user/" + order.getUserId();
String url = "http://user-service/user/" + order.getUserId();
//3.请求user服务器
User user = restTemplate.getForObject(url, User.class);
//4.封装用户信息
order.setUser(user);
//5.返回
return order;
}
}
4.Ribbon负载均衡
4.1负载均衡原理
SpringCloud底层其实时利用了一个叫Ribbon的组件实现
LoadBalancerInterceptor这个类根据service名称,获取到了服务实例的ip和端口。这个拦截器会在对RestTemplate的请求进行拦截获取请求uri和服务id,然后根据请求uri和服务id获取ILoadBalance,ILoadBalance会携带服务id从Eureka中获取服务列表并保存
4.2负载均衡策略IRule
IRule利用内置负载均衡规则Rule(默认是RoundRobinRule轮询),从保存的服务列表中选择一个地址.
4.3总结
4.4负载均衡策略
负载均衡的规则都定义在IRule接口中,而IRule有很多不同的实现类:
-
RoundRobinRule简单轮询
-
AvailabilityFilteringRule短路过滤策略
对以下两种服务器进行忽略:
(1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。
(2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。
-
WeightedResponseTimeRule时间权重响应策略
为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择
-
ZoneAvoidanceRule eureka默认的实现,是一种轮询方案
-
BestAvailableRule最短路策略
-
RandomRule随机策略
-
RetryRule重试策略
4.5自定义负载均衡策略
通过定义IRule实现可以修改负载均衡规则,有两种方式:
1.代码方式,在配置类中注册一个IRule并保存到IOC容器中
@Bean
public IRule randomRule(){
return new RandomRule();
}
2.配置文件方式
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
注意代码方式设置的负载均衡规则会在所用微服务生效
5.Nacos注册中心
5.1什么是Nacos
Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件.相比eureka他的功能更加丰富,界面更加友好
5.2服务注册到nacos
Nacos是SpringCloudAlibaba的组件,而SpringCloudAlibaba也遵循SpringCloud中定义的服务注册、服务发现规范。因此使用Nacos和使用Eureka对于微服务来说,并没有太大区别。
1.引入依赖
在父工程中的
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
然后在微服务中引入nacos-discovery依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
eureka和nacos只能使用一个,此处要注释掉eureka相关依赖
2.编写配置文件
spring:
cloud:
nacos:
server-addr: localhost:8848
此处eureka相关配置也要注释
3.重启微服务
5.3服务分级存储模型
每一个微服务可以有多个实例,这些实例部署在全国各个不同的机房,nacos就将每一个机房类的实例划分为一个集群
在服务间相互访问时,访问本地集群实例和其他实例相比速度更快,所以应该尽可能访问同集群实例
5.3.1如何配置同集群优先的负载均衡策略
因为默认的ZoneAvoidanceRule不能实现同集群优先的负载均衡策略,所用nacos提供了NacosRule来实现.
1.配置服务集群
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ # 集群名称
2.修改负载均衡策略
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
5.4权重配置
权重值越高,该实例的调用机会越大
通过nacos注册中心界面对应服务编辑里修改配置
5.5环境隔离
在程序开发上线过程中,会有各种不同的环境,如何实现各个环境之间的服务的隔离功能?
Nacos提供了namespace来实现环境隔离功能。
- nacos中可以有多个namespace
- namespace下可以有group、service等
- 不同namespace之间相互隔离,例如不同namespace的服务互相不可见
5.5.1创建命名空间
在nacos界面创建
5.5.2给微服务配置命名空间
修改配置文件
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ
namespace: 492a7d5d-237b-46a1-a99a-fa8e98e4b0f9 # 命名空间,填ID
6.Eurka和Nacos的区别
Nacos的服务实例分为两种类型:
- 临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型。
- 非临时实例:如果实例宕机,不会从服务列表剔除,也可以叫永久实例。
配置一个服务实例为永久实例:
spring:
cloud:
nacos:
discovery:
ephemeral: false # 设置为非临时实例
Nacos和Eureka整体结构类似,服务注册、服务拉取、心跳等待,但是也存在一些差异:
Nacos与eureka的共同点
- 都支持服务注册和服务拉取
- 都支持服务提供者心跳方式做健康检测
Nacos与Eureka的区别
- Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
- 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
- Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
- Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式
7.Nacos配置管理
7.1统一配置管理
当微服务部署的实例越来越多,逐个对微服务配置进行修改,容易出错,效率低,所以有了统一的配置管理方案,集中管理所有实例.
7.1.1在nocos中添加配置文件
点击配置管理添加配置文件
填写表单中的信息
注意:在nacos中配置文件中一般配置一些需要热更新的配置,其他基本配置还是存在本地比较好
7.1.2从微服务中拉取配置
在微服务进行nacos配置管理过程中,微服务需要拉取nacos中管理的配置文件并与本地的配置合并,才能完成项目的启动
之前学习中,我们通过application.yml中配置注册中心地址,让微服务获取nacos地址,但配置管理过程中,没有读取application.yml文件,那么微服务怎么得到nacos的地址的?
spring引入了一种新的配置文件先于application.yml文件读取.
1.引入nacos-config依赖
<!--nacos配置管理依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2.添加bootstrap.yaml
在微服务中添加一个bootstrap.yaml文件,将原先配置在application.yml中的nacos注册中心地址,服务名称,开发环境等配置添加到该文件中,并设置需要拉取的nacos中配置文件的后缀名
spring:
application:
name: user-service # 服务名称
profiles:
active: dev #开发环境,这里是dev
cloud:
nacos:
server-addr: localhost:80 # Nacos地址
config:
file-extension: yaml # 文件后缀名
# namespace: 0e3fe3a4-3d80-4e92-b2f3-355d87e01ba4
discovery:
cluster-name: HZ
# namespace: 0e3fe3a4-3d80-4e92-b2f3-355d87e01ba4
注意:如果不是在nacos默认命名空间创建的配置文件,那么需要命名空间id
在这里先通过spring.cloud.nacos.sever-addr获取地址,再根据 ${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}作为文件id,来读取配置。
7.2配置热更新
前面我们说到一般只把需要热部署的配置部署再nacos中,这里我们可以使用两种方式进行热部署
1.在@value注入变量所在类上加上@RefreshScope
2.使用@configurationProperties,并添加一个类封装读取到的配置文件中的属性
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@Data
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
private String dateformat;
}
然后在使用变量的类上利用@Autowired注解注入这个类对象调用其读取到的属性
7.3配置共享
微服务启动时,不只读取一个nacos中的配置文件,如果创建的配置文件没有包含环境就可以被多个环境共享
如果多个配置文件中存在相同的属性时,会根据优先级读取属性:
设置当前环境的nacos配置文件>共享环境的nacos配置文件>本地配置
8.Feign远程调用
8.1使用Feign替代RestTplate
在之前的学习中,我们使用了RestTemplate向其他微服务发送http请求来做远程调用
这种方式的代码方式存在问题:
1.代码可读性差,编程体验不统一
2.参数如果比较复杂,那么url难以维护
所以我们学习用Feign来替代RestTemplate
1.引入openFeign起步依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.启动类添加@EnableFeignClients注解,开启Feign功能
3.创建一个接口,编写Feign客户端
import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
这个客户端主要基于springMVC的注解来声明远程调用的信息,这样Feign就可以帮我们发送http请求了,不需要使用RestTemplate
4.最后在需要使用Feign发送请求的地方注入Feign客户端对象调用对应方法传入相应参数即可完成微服务调用
8.2Feign自定义配置
Feign支持很多自定义配置
- feign.Logger.Level:修改日志级别(四种级别默认NONE,BASIC,HEADERS,FULL)
- feign.codec.Decoder:响应结果解析器
- feign.codec.Encoder:请求参数编码
- feign. Contract:支持注解格式,默认springMVC注解
- feign. Retryer:失败重试机制(Feign调用失败时是否开启重试,默认关闭,利用的是Ribbon的重试)
一般情况下默认就能满足我们的需求,如果需要我们自定义可以自己利用@Bean注解覆盖掉默认Bean
8.2.1自定义Feign日志级别
日志的级别分为四种:
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
修改方式有两种:
方式一:编写配置文件
针对单独微服务配置日志级别
feign:
client:
config:
userservice: # 针对某个微服务的配置
loggerLevel: FULL # 日志级别
针对所有服务
feign:
client:
config:
default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
方式二:在配置类种注入Bean覆盖
public class DefaultFeignConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.BASIC; // 日志级别为BASIC
}
}
如果要让其单独对某个服务生效将其放在对应的Feign客户端中的@FeignClient注解中
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class)
如果要全局生效,则需要放在启动类的@EnableFeignClients注解中
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class)
8.3Feign优化
关于Feign的优化我们可以从两个方面解决:
Feign底层发起http请求依赖于其他框架,
其中包括:
URLConnection:默认实现,无连接池
HttpClient:支持连接池
OKHttp:支持连接池
因此我们可以使用支持连接池的框架发送http请求,来提高效率
这里我们使用HttpClient
1.引入Feign-httpClient依赖
<!--httpClient的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
2.配置连接池
feign:
client:
config:
default: # default全局的配置
loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
httpclient:
enabled: true # 开启feign对HttpClient的支持
max-connections: 200 # 最大的连接数
max-connections-per-route: 50 # 每个路径的最大连接数
方式二:尽量降低Feign的日志输出级别
输出的日志越少,效率越高
8.4最佳实践
经过上面的使用,我们可以发现Feign客户端与服务提供者controller层的代码很相似,那么是否有方法可以简化这种编写呢
我们可以创建一个新的模块将跟feign有关的类和接口抽取出来
1.创建一个新的模块feign-api
2.引入openfeign依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.抽取FeignClient,相关实体类,和自定义配置类
4.删除原先消费者模块中抽取出来的类和接口,删除openfeign依赖,引入feign-api依赖,修改相关导包
5.解决包扫描问题
在服务消费者中是默认是扫描不到feign-api中定义的FeignClient的,所以需要指定包扫描位置或手动指定字节码文件.
使用basePackages属性指定扫描位置(批量扫描)
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
使用Clients属性指定需要加载的接口(内容为数组,可以指定多个)
@EnableFeignClients(clients = {UserClient.class})
9.Gateway网关服务
9.1为什么使用网关?
在前面的学习种我们知道了内部微服务是通过feign
来进行服务调用的.
而Gateway网关是我们所有微服务的统一入口,
网关的核心特性功能有:
-
权限控制:校验请求用户是否有请求资格,没有就进行拦截
-
请求路由:一切请求都必须经过gateway,但网关不做业务处理,而是根据其设置的断言规则,把请求转发到指定的某个微服务,这个过程叫做路由,当路由目标服务有多个实例时,需要做负载均衡
-
限流:当请求压力过大时,在网关种可以按指定的速度下放请求来避免服务压力过大
在SPringCloud种有两种网关技术实现:
1.gateway
2.zuul
zuul是基于servlet的实现,属于阻塞式编程
gateway是基于WebFlux,属于响应式编程
9.2创建gateway网关服务
1.创建一个新的模块
2.引入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>
3.编写启动类
4.编写基础配置和路由规则
server:
port: 10010 # 网关端口
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
gateway:
routes: # 网关路由配置
- id: user-service # 路由id,自定义,只要唯一即可
# uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
- Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
9.3网关路由配置和断言工厂
- 路由id:路由的唯一标示
- 路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
- 路由断言(predicates):判断路由的规则,
- 路由过滤器(filters):对请求或响应做处理
在上面创建gateway网关的配置过程种我们使用了
路由断言(是一种布尔表达形式)规则实际上只是字符串,断言工厂 predicates factory 可以读取断言,转变为相应的判断条件
像Path的这种断言工厂SpringCloudGateway还有10多个
9.4过滤工厂
GatewayFilter是网关提供的一种过滤器,用来对进入网关的请求和响应做处理
spring提供了多种不同的路由过滤工厂
我们以AddRequestHeader为例添加路由过滤:
在routes下配置的过滤器只在路由对应微服务时生效
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
filters: # 过滤器
- AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
定义默认过滤器,对经过网关的所有路由生效
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
default-filters: # 默认过滤项
- AddRequestHeader=Truth, Itcast is freaking awesome!
9.5全局过滤器
之前我们学习了网关提供的过滤器,但这些过滤器作用都是固定的,如果我们希望拦截请求,做自己的业务逻辑就没办法实现
我们可以通过定义全局过滤器来做自己的业务逻辑,作用和GatewayFilter一样,区别在于,业务逻辑不固定,需要自级代码实现
接下来我们实现一个全局过滤器做一个简单的权限管理
实现GlobalFilter接口重写filter方法,两个参数分别是交换机ServerWebExchange和GatewayFilterChain路由过滤器链
@Component
@Order(0)//指定优先级
public class AuthorizationFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取请求响应对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//2.获取请求参数
MultiValueMap<String, String> params = request.getQueryParams();
String authorization = params.getFirst("authorization");
//3.判断参数是否存在
if (StringUtils.isEmpty(authorization)) {
//说明参数不存在响应错误信息
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//4.判断参数值是否是admin
if (!StringUtils.equals(authorization,"admin")) {
//说明不是admin返回错误信息
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//5.放行到下一个Filter
return chain.filter(exchange);
}
}
9.6跨域问题
跨域:网页所在域和ajax请求所在域不同,就是跨域
- 协议不同 :
http://
和file://
是不同的域 - 域名不同: www.taobao.com 和 www.taobao.org 是不同的域
- IP不通 :
http://192.168.200.100
和http://192.168.200.200
是不同的域 - 端口不同:localhost:8080和localhost8081 是不同的域
跨域问题:浏览器禁止请求发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
解决:在gateway服务的配置文件中添加如下配置
spring:
cloud:
gateway:
# 。。。
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
10.微服务保护Sentinel
10.1雪崩问题
微服务调用链路种某个服务故障,引起整个链路的所有微服务都不可用.
解决方案:
1.超时处理:设定一个超时时间,超过一段时间没有响应就返回错误信息,不会无休止等待
2.舱壁模式:限定每个业务能使用的线程数,避免耗尽整个tomcat资源,也叫线程隔离
3.熔断降级:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截一切该业务请求.
4.流量控制:限制业务访问的QPS,避免服务因流量的突增而故障.
10.2微服务整合Sentinel
1.引入依赖
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2.配置控制台地址
server:
port: 8080
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
3.调用任意端点,触发监控
10.4Sentinel实现流量控制
10.4.1簇点链路和限流
簇点链路:当请求进入微服务时,首先会访问DispatcherServlet,然后进入Controller、Service、Mapper,这样的一个调用链就叫做簇点链路。
链路中被监控的每一个接口就是一个资源
1.打开资源,弹出表单
2.填写限流规则
QPS代表每秒的最大请求访问量
可以时使用jmeter压测工具进行测试
10.4.2流控模式
高级选项中有三种流控模式
-
直接:统计当前资源请求,直接对当前资源进行限流
-
关联:统计与当前资源关联的另一个资源,触发阈值时,对当前资源限流
-
链路:统计链路访问入口访问到当前资源的请求,触发阈值时,对指定链路限流
10.4.3流控效果
指定请求达到阈值后采取的措施:
-
快速失败:达到阈值后,新的请求会被立即拒绝并抛出异常
-
warm up:预热模式,对超出阈值的请求同样拒绝并抛出异常,但阈值是动态变化的,从小变到最大阈值
-
排队等待:让所有请求按照先后次序排好队,两个请求间隔不能小于指定时长
10.4.4warm up
阈值一般是一个微服务能承担的最大QPS,但是一个服务刚刚启动时,一切资源尚未初始化(冷启动),如果直接将QPS跑到最大值,可能导致服务瞬间宕机。
warm up也叫预热模式,是应对服务冷启动的一种方案。请求阈值初始值是 maxThreshold / coldFactor,持续指定时长后,逐渐提高到maxThreshold值。而coldFactor的默认值是3.
10.4.5排队等待
当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。
而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
如果使用队列模式做流控,所有进入的请求都要排队,以固定的200ms的间隔执行,QPS会变的很平滑:
10.4.6流控效果总结
-
快速失败:QPS超过阈值时,拒绝新的请求
-
warm up: QPS超过阈值时,拒绝新的请求;QPS阈值是逐渐提升的,可以避免冷启动时高并发导致服务宕机。
-
排队等待:请求会进入队列,按照阈值允许的时间间隔依次执行请求;如果请求预期等待时长大于超时时间,直接拒绝
10.5隔离和降级
前面我们学习了避免因为高并发而引起的服务故障,但服务还会因为其他原有故障
这里我们学习两个新手段:
线程隔离和熔断降级
10.5.1Feign整合Sentinel
1.修改配置开启sentinel功能
feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
2.编写失败降级逻辑
一般降级都是返回一个默认值
这里我们采用可以做异常处理的FallbackFactory
先在feign-api项目中定义类实现FallbackFactory
import cn.itcast.feign.client.FeignUserClient;
import cn.itcast.feign.pojo.User;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<FeignUserClient> {
@Override
public FeignUserClient create(Throwable cause) {
return new FeignUserClient() {
@Override
public User queryById(Long id) {
log.error("服务出现问题,id{}",id);
return new User();
}
};
}
}
然后将实现类注册成一个Bean
@Bean
public UserClientFallbackFactory userClientFallbackFactory(){
return new UserClientFallbackFactory();
}
最后在FeignClient中使用FallbackFactory实现类
import cn.itcast.feign.clients.fallback.UserClientFallbackFactory;
import cn.itcast.feign.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
10.5.2线程隔离
线程隔离有两种实现方式:
第一种线程池隔离:
调用者在调用服务提供者时,给每个调用的请求分配独立线程池,出现故障时,最多消耗这个线程池内资源,避免把调用者的所有资源耗尽。
第二种:信号量隔离:
信号量隔离:不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求。
在前面做限流操作时,有两个参数设置阈值
其中对线程数做限制,实现的就是线程隔离
10.5.3熔断降级
熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。
断路器的熔断和放行都是通过状态机来完成的
状态机有三种状态:
closed:关闭状态:断路器放行所有请求,统计异常比例,慢请求笔记,超过阈值进入open状态.
open:服务熔断,访问进来的请求会被拒接,快速失败,直接走降级逻辑.5秒后进入half-open状态
half-open:半开状态,放行一次请求,根据执行结果切换状态:
请求成功:closed
请求失败:open
断路器熔断策略有三种:慢调用,异常比例,异常数
10.5.4熔断策略
慢调用:当业务响应时长超过设置的最大时长,被认定为慢请求,如果请求数量超过设定的最小请求数量,就会触发熔断.
异常比例和异常数
统计指定时间内的调用,如果调用次数超过指定请求数,并且异常的比例或数量超过阈值,触发熔断
标签:服务,请求,springCloud,nacos,eureka,实例,cloud From: https://www.cnblogs.com/zlsame/p/17432729.html