Springclound常用五大组件及其使用原理

注册中心Eureka

  • Eureka-Server:就是服务注册中心(可以是一个集群),对外暴露自己的地址。

  • 提供者:启动后向Eureka注册自己信息(地址,服务名称等),并且定期进行服务续约

  • 消费者:服务调用方,在使用的时候去拉取一次,之后会定期去Eureka拉取服务列表,然后使用负载均衡算法选出一个服务进行调用。

  • 心跳(续约):提供者和消费者定期通过http方式向Eureka刷新获取(默认30秒)

如果消费者之前已经调用过注册中心获取过提供者的地址信息,那么注册中心宕机后,在提供者地址不变的前提下,不影响服务直接调用

引入SpringCloud为eureka提供的starter依赖:

XML 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

编写一个启动类

给eureka-server服务编写一个启动类,一定要添加一个@EnableEurekaServer注解,开启eureka的注册中心功能:

java 复制代码
@SpringBootApplication
@EnableEurekaServer  //开启eureka的注册中心功能
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

编写一个application.yml文件,内容如下:

javascript 复制代码
server:
  port: 10086
spring:
  application:
    name: eureka-server
eureka:
  client:
    service-url: 
      defaultZone: http://127.0.0.1:10086/eureka
    register-with-eureka: false # 自己不注册到eureka中
    fetch-registry: false #自己不从eureka中拉取别的服务

启动微服务,然后在浏览器访问:http://127.0.0.1:10086

将微服务注册到注册中心->引入依赖->编写配置文件,名字要指定

javascript 复制代码
spring:
  application:
    name: userservice
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

Ribbon负载均衡

java 复制代码
@SpringBootApplication
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
    //注册RestTemplate,第三方的类,交给IOC容器管理
    @Bean
    @LoadBalanced//自动负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@LoadBalanced

在远程调用的RestTemplate,上添加了@LoadBalanced注解,即可实现负载均衡功能

原理:SpringCloud底层其实是利用了一个名为Ribbon的组件,来实现负载均衡功能的。

SpringCloudRibbon的底层采用了一个拦截器,拦截了RestTemplate发出的请求,对地址做了修改。用一幅图来总结一下:

基本流程如下:

  • 拦截我们的RestTemplate请求http://userservice/user/1

  • RibbonLoadBalancerClient会从请求url中获取服务名称,也就是user-service

  • DynamicServerListLoadBalancer根据user-service到eureka拉取服务列表

  • eureka返回列表,localhost:8081、localhost:8082

  • IRule利用内置负载均衡规则,从列表中选择一个,例如localhost:8081

  • RibbonLoadBalancerClient修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1,发起真实请求

Hystrix 熔断器

雪崩问题

微服务中,服务间调用关系错综复杂,一个请求,可能需要调用多个微服务接口才能实现,会形成非常复杂的调用链路:Hystix解决雪崩问题的手段主要是服务降级和线程隔离;

服务降级的演示

第一步:导依赖

XML 复制代码
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
 </dependency>

第二步:在引导类上开启Hystrix

java 复制代码
@EnableCircuitBreaker//开启断路器
@SpringBootApplication
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
     //注册RestTemplate,第三方的类,交给IOC容器管理
    @Bean
    @LoadBalanced//自动负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

第三步:开发一个方法作为降级的处理方式要求:方法的参数和返回结果类型要和原方法findById应该一致

java 复制代码
public String queryOrderByIdFallback(Long orderId) {
    return "服务器太忙,请稍后重试........";
}

第四步:修改原有的方法,在方法上添加注解

全局降级

上面的写法中每个方法都需要一个降级方法,很麻烦,我们可以使用一个全局的降级方案

java 复制代码
@Service
@DefaultProperties(defaultFallback="allMethodFallBack")
//在类上添加一个注解  @DefaultProperties(defaultFallback="allMethodFallBack")
public class OrderService {
    @HystrixCommand//指向降级方法
    public String queryOrderById() {    
        return "我出错了";
    }
    //    全局降级方法
    public String allMethodFallBack() {
        return "服务器太太太太太太太忙,请稍后重试.....";
    }
}

超时设置

Hystix的默认超时时长为1秒,如果这个微服务响应时间超过1秒,就会报错,降级处理,但是有的方法本身需要执行的时间就长,自定义超时时间

javascript 复制代码
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000 # 超时时间3秒

熔断原理

熔断功能:默认5秒内发起20次请求,失败率超过50% 进入熔断状态 5秒内的其他时间都会直接进入降级方法

熔断器有3种状态:open close half-open

状态机有3个状态:

  • Closed:关闭状态(断路器关闭),所有请求都正常访问。

  • Open:打开状态(断路器打开),所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全关闭。默认失败比例的阈值是50%,请求次数最少不低于20次。

  • Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放1次请求通过,若这个请求是健康的,则会关闭断路器,否则继续保持打开,再次进行5秒休眠计时。

Feign:封装了RestTemple 远程调用

导入依赖

XML 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

创建interface接口

java 复制代码
@FeignClient("userservice")
public interface UserClient {

    @GetMapping("/user/{id}")  //这里的返回结果和 url地址一定要和提供方保持一致
    User findById(@PathVariable("id") Long id);
}
  • 首先这是一个接口,Feign会通过动态代理,帮我们生成实现类。这点跟mybatis的mapper很像

  • @FeignClient,声明这是一个Feign客户端,同时通过value属性指定服务名称

  • 接口中的定义方法,完全采用SpringMVC的注解,Feign会根据注解帮我们生成URL,并访问获取结果

开启Feign功能

java 复制代码
@EnableFeignClients//开启Feign
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

}

负载均衡

Feign中本身已经集成了Ribbon依赖和自动配置:

Feign默认也有对Hystix的集成:

javascript 复制代码
feign:
  hystrix:
    enabled: true # 开启Feign的熔断功能
  • 首先,我们要定义一个类,是在刚才编写的UserFeignClient,作为fallback的处理类
java 复制代码
import cn.itcast.order.pojo.User;
import org.springframework.stereotype.Component;

@Component
public class UserClientFallback implements UserFeignClient {
    @Override
    public User findById(Long id) {
        User user = new User();
        user.setId(0L);
        user.setUsername("用户查询出现异常!");
        return user;
    }
}
  • 然后在UserFeignClient中,指定刚才编写的实现类,就实现了降级方法
java 复制代码
@FeignClient(value = "userservice", fallback = UserClientFallback.class)
public interface UserFeignClient {

    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

Gateway网关

网关的核心功能是:路由和过滤(鉴权)

  • 搭建网关
XML 复制代码
<dependencies>
    <!--引入gateway 网关-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
</dependencies>
  • 编写启动类
java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApiGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class,args);
    }

}
  • 编写配置
javascript 复制代码
server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    # 网关配置
    gateway:
      # 路由配置:转发规则
      routes: #集合。
      # id: 唯一标识。默认是一个UUID
      # uri: 转发路径
      # predicates: 条件,用于请求网关路径的匹配规则
      - id: userservice
        uri: http://localhost:8081/
        predicates:
        - Path=/user/**

application.yml 中的uri是写死的,就是Gateway-静态路由

Gateway-动态路由

XML 复制代码
<dependency>
    <--添加注册中心Eureka--> 
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

修改配置文件

javascript 复制代码
server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    # 网关配置
    gateway:
      # 路由配置:转发规则
      routes: #集合。
      # id: 唯一标识。默认是一个UUID
      # uri: 转发路径
      # predicates: 条件,用于请求网关路径的匹配规则
      - id: user-service
        uri: lb://userservice # lb负载均衡
        predicates:
        - Path=/user/**
       
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka

Gateway-过滤器

GatewayFilter :局部过滤器,针对单个路由 GlobalFilter :全局过滤器,针对所有路由

全局过滤器

  • GlobalFilter 全局过滤器,不需要在配置文件中配置,系统初始化时加载,并作用在每个路由上。

  • Spring Cloud Gateway 核心的功能也是通过内置的全局过滤器来完成。

  • 自定义全局过滤器步骤:

    1. 定义类实现 GlobalFilter 和 Ordered接口

    2. 复写方法

    3. 完成逻辑处理

java 复制代码
/**
 * 自定义全局过滤器
 */
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
  //需求 :请求参数中带有token 内容,就放行,否则提示未授权
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//        获取请求对象
        ServerHttpRequest request = exchange.getRequest();
//        获取参数
        MultiValueMap<String, String> valueMap = request.getQueryParams();
        List<String> valList = valueMap.get("token");
        if(CollectionUtils.isEmpty(valList)){//异常情况,没有token参数
            // 获取响应对象
            ServerHttpResponse response = exchange.getResponse();
            // 返回响应码
            response.setStatusCode(HttpStatus.FORBIDDEN);//401
            // 完成响应
            return response.setComplete();
        }
        return chain.filter(exchange);//放行
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
相关推荐
偷萧逸苦茶14 分钟前
docker常见命令
docker·容器·eureka
高山莫衣14 小时前
Docker Desktop导致存储空间不足时的解决方案
docker·容器·eureka
LuckyLay14 小时前
Compose 常用命令详解——AI教你学Docker
docker·容器·eureka
没有名字的小羊15 小时前
7.可视化的docker界面——portainer
docker·容器·eureka
白仑色21 小时前
Spring Cloud 微服务(统一网关设计)
spring cloud·微服务·服务治理·统一配置管理·分布式配置中心
述雾学java1 天前
Spring Cloud 服务追踪实战:使用 Zipkin 构建分布式链路追踪
分布式·spring·spring cloud·zipkin
被困者10 天前
Linux部署Sonic前后端(详细版)(腾讯云)
spring cloud·云原生·eureka
Hellyc10 天前
springcloud/springmvc协调作用传递验证信息
后端·spring·spring cloud
Ken_111511 天前
SpringCloud系列(32)--使用Hystrix进行全局服务降级
spring cloud·hystrix
Ken_111511 天前
SpringCloud系列(33)--使用Hystrix进行通配服务降级
spring cloud·hystrix