首页 > 其他分享 >springCloud

springCloud

时间:2023-05-25 20:23:18浏览次数:31  
标签:服务 请求 springCloud nacos eureka 实例 cloud

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.引入依赖

在父工程中的中引入SpringCloudAlibaba的依赖:

<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的共同点

  1. 都支持服务注册和服务拉取
  2. 都支持服务提供者心跳方式做健康检测

Nacos与Eureka的区别

  1. Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
  2. 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
  3. Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
  4. 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网关路由配置和断言工厂

  1. 路由id:路由的唯一标示
  2. 路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
  3. 路由断言(predicates):判断路由的规则,
  4. 路由过滤器(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.100http://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

相关文章

  • springcloud 第一个项目遇到的报错
    在创建模块springcloud-provider-8001的时候出现两个错误,一个是CLASSNOTFOUND,另一个是错误的类文件:/D:/apache-maven-3.8.8/maven-repo/org/springframework/spring-beans/6.0.4/spring-beans-6.0.4.jar!/org/springframework/beans/factory/annotation/Autowired.class   ......
  • 【服务治理】基于SpringCloudAlibaba微服务组件的灰度发布设计(二)
    一.背景在上文中,灰度发布遇到了些问题,例如:1.动态修改Nacos配置中心的元数据信息,如何同步到Nacos注册中心对应服务的列表中2.管理后台业务调用其它服务灰度实例时的路由规则二.解决方案//TODO ......
  • 微服务框架SpringCloud-2-服务拆分及远程调用-2.2服务远程调用
    微服务框架SpringCloud2服务拆分及远程调用2.2服务远程调用2.2.1根据订单id查询订单功能需求:根据订单id查询订单的同时,把订单所属的用户信息一起返回现在是不能做到的, 可以看到现在user是个null【所以没有达到需求】现在的情况是下面的样子  但是需求想要的......
  • 微服务框架SpringCloud微-2-服务拆分及远程调用-demo黑马
    微服务框架SpringCloud微服务架构2服务拆分及远程调用2.1案例Demo2.1.1服务拆分注意事项 这里四个模块,拆成四个服务就行了 单一职责:不同微服务,不要重复开发相同业务【不能像以前那样了】数据独立:不要访问其它微服务的数据库 3.面向服务:将自己的业务暴......
  • 微服务与springcloud的介绍
    1.什么是微服务?随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构。微服务是一种经过良好架构设计的分布式架构方案。微服务的上述特性其实是给分布式架构制定一个标准,进一步降低服务之间的耦合,提供服务的独立性和灵活性。做到高......
  • springCloud Alibaba服务的注册与发现之eureka客户端注册
    1、在客户端maven项目中添加eureka客户端依赖<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>2、在工程application.yml文件中......
  • SpringCloud Sleuth分布式链路请求跟踪
    ✨SpringCloudSleuth分布式链路请求跟踪基本介绍为什么需要使用SleuthSleuth基本介绍zipkin基本介绍zipkin服务端搭建启动zipkin服务提供者(**cloud-provider-payment8001)**修改pom文件ymlController(**PaymentController)**服务消费者(cloud-consumer-order80)pom文件yml**......
  • 四、SpringCloud Alibaba搭建Nacos集群
    Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,强烈不建议部署在公共网络环境。Nacos支持三种部署模式单机模式-用于测试和单机试用。集群模式-用于生产环境,确保高可用。多集群模式-用于多数据中心场景。单机模式下运行NacosLi......
  • 一天吃透SpringCloud面试八股文
    1、什么是SpringCloud?Springcloud流应用程序启动器是基于SpringBoot的Spring集成应用程序,提供与外部系统的集成。SpringcloudTask,一个生命周期短暂的微服务框架,用于快速构建执行有限数据处理的应用程序。SpringCloud各个微服务之间为什么要用http交互?难道不慢吗?Spr......
  • 05-面试必会-SpringBoot&SpringCloud
    01-讲一讲SpringBoot自动装配的原理1.在SpringBoot项目的启动引导类上都有一个注解@SpringBootApplication这个注解是一个复合注解,其中有三个注解构成,分别是@SpringBootConfiguration:是@Configuration的派生注解,标注当前类是一个SpringBoot的配置类@......