SpringCloud(注册中心+OpenFeign+网关+配置中心+服务保护+分布式事务)

基本概念

SpringCloud是一个基于Spring Boot的微服务架构解决方案,它提供了一系列的工具和组件来帮助开发者构建和管理微服务系统。以下是对SpringCloud中注册中心、OpenFeign、网关、配置中心、服务保护、分布式事务的详细介绍:

注册中心

注册中心是微服务架构中的服务发现机制的核心实现。它允许服务在启动时将自身信息(如IP地址和端口)注册到注册中心,其他服务通过注册中心查找服务实例,实现服务间的动态通信。注册中心的主要功能包括服务注册、服务发现以及健康检查。常见的注册中心组件有Eureka、Consul、Zookeeper和Nacos等。

  • Eureka:是Spring Cloud官方推荐的注册中心,具有轻量级实现的特点。Eureka Server作为注册中心,管理所有服务的注册信息;Eureka Client作为微服务的客户端,通过Eureka Server注册和获取服务。
  • Consul:支持健康检查和键值存储,功能较为丰富。
  • Zookeeper:一致性保证较强,但复杂度较高,适用于对一致性要求较高的场景。
  • Nacos:由阿里巴巴开源,支持配置管理和服务发现,是近年来较为流行的注册中心之一。

OpenFeign

OpenFeign是Spring Cloud生态系统中的一个声明式Web服务客户端,它使得编写Web服务客户端变得更加容易。

  • 特点:OpenFeign允许开发者通过定义Java接口和使用注解的方式来声明HTTP请求,从而简化了与HTTP服务的交互。开发者只需定义一个接口并使用注解来声明请求参数、请求方式、请求头等信息,OpenFeign会自动生成代理对象来处理HTTP请求。此外,OpenFeign还支持可插拔的编码器和解码器、多种注解以及集成Spring MVC注解等特性。
  • 集成:Spring Cloud OpenFeign集成了Eureka、Spring Cloud CircuitBreaker以及Spring Cloud LoadBalancer等组件,以便在使用Feign时提供一个负载均衡的HTTP客户端。
  • 使用:在Spring Boot项目中,可以通过添加spring-cloud-starter-openfeign依赖,并在主启动类上添加@EnableFeignClients注解来启用OpenFeign。然后,通过创建一个接口并使用@FeignClient注解来声明远程服务的调用即可。

网关

Spring Cloud Gateway是一个基于Spring和Spring Boot构建的轻量级网关服务,用于构建微服务架构中的API网关。

  • 功能:Spring Cloud Gateway支持动态路由、过滤器、负载均衡、集成服务发现以及安全认证等功能。它可以根据不同的请求路径将请求转发到不同的目标服务,并通过过滤器链对请求和响应进行处理和修改。
  • 使用:在Spring Boot项目中,可以通过添加spring-cloud-starter-gateway依赖来引入Spring Cloud Gateway。然后,在配置文件中添加路由配置,指定请求的匹配规则和目标服务的转发地址。最后,在主启动类上添加@EnableGateway注解(注意:实际上应为@SpringBootApplication注解,因为Spring Cloud Gateway的自动配置已经包含了网关的启用逻辑)来启用Spring Cloud Gateway的功能。

配置中心

Spring Cloud配置中心(Spring Cloud Config)是用于管理分布式系统中的外部配置的解决方案。

  • 功能:Spring Cloud Config提供了服务器端和客户端支持,用于集中化的外部配置。它支持集中管理配置、实时刷新配置、版本管理以及环境隔离等功能。
  • 架构:Spring Cloud Config分为Config Server和Config Client两部分。Config Server作为配置服务器,集中管理配置文件,并向客户端提供配置;Config Client作为配置客户端,从配置服务器获取并应用配置。
  • 使用:在搭建Config Server时,需要创建一个Spring Boot项目,并添加spring-cloud-config-server等依赖。然后,在配置文件中指定配置文件的存储位置(如Git仓库)。在搭建Config Client时,需要添加spring-cloud-starter-config等依赖,并在bootstrap.yml或application.yml配置文件中指定Config Server的地址和要获取的配置信息。(也可采用Nacos做配置中心)

服务保护

服务保护是微服务架构中保障服务稳定性和可用性的重要手段。Spring Cloud提供了多种服务保护组件和策略,如断路器(Hystrix)、限流器(Sentinel)等。

  • 断路器:断路器是一种用于防止分布式系统中出现级联故障的保护机制。当某个服务调用失败或响应时间过长时,断路器会迅速切断对该服务的调用请求,从而避免整个系统崩溃。Spring Cloud Hystrix是一个常用的断路器实现。
  • 限流器:限流器用于控制服务的并发请求数量,防止服务因过载而崩溃。通过限制并发请求的数量,可以确保服务在合理的负载范围内运行。Spring Cloud Alibaba Sentinel是一个常用的限流器实现。

分布式事务

分布式事务是指涉及多个数据源或服务的事务处理机制。在微服务架构中,由于服务之间的调用是异步的、去中心化的,因此分布式事务的处理变得尤为复杂。Spring Cloud提供了多种分布式事务解决方案,如基于消息的最终一致性方案(如Seata)、基于数据库的事务管理器(如Atomikos、Bitronix)等。

  • Seata:Seata是一款开源的分布式事务解决方案,提供了AT、TCC、SAGA和XA事务模式。它通过全局事务ID和分支事务ID来管理分布式事务的执行和回滚。
  • Atomikos:Atomikos是一个提供分布式事务管理的Java库,它支持XA协议和两阶段提交协议(2PC)。通过Atomikos,可以在多个数据源之间实现分布式事务的一致性。
  • Bitronix:Bitronix是另一个提供分布式事务管理的Java库,它也支持XA协议和两阶段提交协议。与Atomikos相比,Bitronix具有更轻量级、更易用的特点。

SpringCloud提供了一套完整的微服务架构解决方案,包括注册中心、OpenFeign、网关、配置中心、服务保护以及分布式事务等组件和特性。这些组件和特性相互协作,共同保障了微服务系统的稳定性、可用性和可扩展性。

实战

父maven依赖

java 复制代码
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2021.0.9</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2021.0.6.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

1、注册中心

bash 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
java 复制代码
spring:
  cloud:
    nacos:
      server-addr: localhost:8848

2、OpenFeign

bash 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
java 复制代码
@SpringBootApplication
// 开启
@EnableFeignClients(basePackages = "com.api.client", defaultConfiguration = DefaultFeignConfig.class)
public class Goods {
    public static void main(String[] args) {
        SpringApplication.run(Goods.class, args);
    }
}
java 复制代码
@FeignClient("cart")
public interface CartClient {
    @GetMapping("/cart/ok/{id}")
    String ok(@PathVariable("id") Long id);
}
java 复制代码
@RestController
@RequestMapping("/goods")
public class GoodsController {

    @Resource
    private CartClient cartClient;

    @GetMapping("/ok/{id}")
    public String ok(@PathVariable("id") Long id) {
        System.out.println(cartClient.ok(id));
        return "goods ok " + id;
    }
}

默认实现不支持连接池

a. 引入连接池

bash 复制代码
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>
java 复制代码
feign:
  httpclient:
    enabled: true

b. 日志

只在FeignClient所在包的日志级别为debug时,才输出日志

  • none:不记录日志(默认)
  • basic:记录请求方法、URL、响应状态码、执行时间
  • headers:在basic基础上,额外记录请求和响应的头信息
  • full:记录所有信息
java 复制代码
public class DefaultFeignConfig {
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    @Bean
    // 请求拦截器
    public RequestInterceptor tokenRequestInterceptor() {
        return template -> template.header("userId", "900");
    }
}
java 复制代码
@FeignClient(configuration = )也可以配置

3、网关

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

a. 路由

bash 复制代码
spring:
  main:
   # 不使用SpringMVC那一套
    web-application-type: reactive
  cloud:
    gateway:
      routes:
        # 路由规则唯一标识
        - id: goods
          # 路由目标微服务地址
          uri: lb://goods
          # 路由判断条件
          predicates:
            # 路由规则名=参数,路由规则名=参数,...,路由规则名=参数(多个)
            # 多个也可以使用yaml的列表语法
            # - Path=/test1/**
            # - Path=/test2/**
            - Path=/goods/**  # 以此前缀即符合
          # 路由过滤器(处理请求或响应)
          filters:
            # 过滤器名=参数
            - AddRequestHeader=a,b
        - id: cart
          uri: lb://cart
          predicates:
            - Path=/cart/**
      # 默认过滤器,对所有路由生效
      default-filters:
        - AddRequestHeader=x,y
        # 自定义的
        - LogAll=100,200,300

b. 登录校验

java 复制代码
@Component
// 全局过滤器,作用在所有路由,声明后自动生效
public class GF implements GlobalFilter, Ordered {

    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    // 不是调用链最后一个就行
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    /*
            参数:
                请求上下文,包含整个过滤器链内共享数据
                过滤器链,当前过滤器执行完后,要调用过滤器链中的下一个过滤器
         */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        System.out.println(request.getHeaders());

        // 路径格式是否符合
        System.out.println(antPathMatcher.match("/goods/**", request.getPath().toString()));

        // 新增请求头(可以传递解析出来的用户信息)
        exchange = exchange.mutate().request(builder -> builder.header("userId", "90")).build();

        // 放行
        return chain.filter(exchange);

        // 拦截终止
        // ServerHttpResponse response = exchange.getResponse();
        // return response.setComplete();
    }
}
java 复制代码
@Component
/*
    路由过滤器,作用在任意指定的路由,默认不生效,需配置到路由
    固定类名后缀,方便配置
 */
public class LogAllGatewayFilterFactory extends AbstractGatewayFilterFactory<LogAllGatewayFilterFactory.ABC> {
    public LogAllGatewayFilterFactory() {
        // 父类负责读配置
        super(ABC.class);
    }

    // 配置无参只需实现这一个方法就好了,否则需要再实现一个方法,并定义一个构造器
    @Override
    public GatewayFilter apply(ABC config) {
        return new OrderedGatewayFilter((exchange, chain) -> {
            System.out.println("路由过滤器执行 " + config);
            return chain.filter(exchange);
        }, 1);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        // 将成员变量名称依次返回,注意顺序,将来读取参数时也按照此顺序
        return Arrays.asList("a", "b", "c");
    }

    // 自定义配置属性,注意成员变量名称
    @Data
    public static class ABC {
        private String a;
        private String b;
        private String c;
    }
}

4、配置中心

bash 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

a. 共享配置

bootstrap.yml

bash 复制代码
spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      config:
        # 要读的文件后缀名
        file-extension: yaml
        # 共享配置
        shared-configs:
          - data-id: same-config-mvc.yaml
          - data-id: same-config-log.yaml
  application:
    name: goods

b. 配置热更新

spring.application.name(微服务名称) - spring.active.profile(项目环境,可选). file-extension(文件后缀名)

使用@ConfigurationProperties加载属性

c. 网关动态路由

bash 复制代码
[
    {
        "id": "goods",
        "uri": "lb://goods",
        "predicates": [
            {
                "name": "Path",
                "args": {
                    "_genkey_0": "/goods/**"
                }
            }
        ],
        "filters": []
    }
    ,
    {
        "id": "cart",
        "uri": "lb://cart",
        "predicates": [
            {
                "name": "Path",
                "args": {
                    "_genkey_0": "/cart/**"
                }
            }
        ],
        "filters": []
    }
]
java 复制代码
@Component
public class DynamicRouter {
    private final Set<String> routeIdSet = new HashSet<>();
    @Resource
    private NacosConfigManager nacosConfigManager;
    @Resource
    private RouteDefinitionWriter routeDefinitionWriter;
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    @PostConstruct
    private void init() throws NacosException {
        // 项目启动时,先拉取配置并添加监听器
        String config = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(
                        "gateway-dynamic-route.json",
                        "DEFAULT_GROUP",
                        5000,
                        new Listener() {
                            // 定义一个线程池
                            @Override
                            public Executor getExecutor() {
                                return null;
                            }

                            // 当配置变更时,需要做的事情
                            @Override
                            public void receiveConfigInfo(String configInfo) {
                                // 更新路由表
                                updateRoutes(configInfo);
                            }
                        }
                );

        // 第一次读取到的配置,也需要更新到路由表
        updateRoutes(config);
    }

    private void updateRoutes(String config) {
        if (!StringUtils.hasText(config)) {
            return;
        }

        List<RouteDefinition> routeDefinitions = JSON.parseArray(config, RouteDefinition.class);
        // 先删旧路由表
        for (String i : routeIdSet) {
            routeDefinitionWriter.delete(Mono.just(i)).subscribe();
        }
        // 清空旧路由id集
        routeIdSet.clear();

        for (RouteDefinition routeDefinition : routeDefinitions) {
            // 更新路由表
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            // 记录路由id,便于下一次更新删除
            routeIdSet.add(routeDefinition.getId());
        }

        // 通知网关更新路由
        applicationEventPublisher.publishEvent(new RefreshRoutesEvent(routeDefinitionWriter));
    }
}

5、服务保护

  • 雪崩:微服务调用链路中某个服务故障,引起整个链路中所有微服务都不可用(级联失败)

  • 请求限流:限制访问微服务的请求并发量(流量整形)

  • 线程隔离(舱壁模式):限定每个业务能使用的线程数量而将故障业务隔离,避免故障扩散

  • 服务熔断:由断路器统计请求异常比或慢调用比,如果超出阈值则熔断业务,拦截该接口的请求;当服务恢复时,断路器会放行访问该服务的请求

  • 失败处理:熔断期间,所有请求快速失败,全都走fallback逻辑

bash 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
bash 复制代码
spring:
  cloud:
    sentinel:
      transport:
        # 控制台地址
        dashboard: localhost:8080
      # 簇点链路中,将请求方式+请求路径作为簇点资源名称(开启请求方式前缀)
      http-method-specify: true

a. fallback

bash 复制代码
feign:
  sentinel:
    enabled: true
java 复制代码
public class CartClientFallbackFactory implements FallbackFactory<CartClient> {
    @Override
    public CartClient create(Throwable cause) {
        System.out.println(cause.getMessage());
        return id -> "wrong call " + id;
    }
}
java 复制代码
@Bean
public CartClientFallbackFactory cartClientFallbackFactory() {
    return new CartClientFallbackFactory();
}
java 复制代码
@FeignClient(value = "cart", fallbackFactory = CartClientFallbackFactory.class)

6、分布式事务

bash 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
bash 复制代码
seata:
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace:
      group: SEATA_GROUP
      application: seata-server
      username:
      password:
  # 事务组名称
  tx-service-group: mall
  service:
    # 事务组与tc集群的映射
    vgroup-mapping:
      mall: default
  # data-source-proxy-mode: XA
  data-source-proxy-mode: AT
java 复制代码
// 全局事务的入口方法添加
@GlobalTransactional
相关推荐
韩立学长10 小时前
【开题答辩实录分享】以《自助游网站的设计与实现》为例进行选题答辩实录分享
java·mysql·spring
不像程序员的程序媛10 小时前
Spring的cacheEvict
java·后端·spring
谷哥的小弟11 小时前
Spring Framework源码解析——PropertiesLoaderUtils
java·后端·spring·框架·源码
云和数据.ChenGuang12 小时前
OpenEuler系统下RabbitMQ安装与基础配置教程
服务器·分布式·rabbitmq·ruby·数据库运维工程师·运维教程
岳轩子14 小时前
DDD领域驱动设计:核心概念、实践结构与框架对比
java·spring
何中应14 小时前
Bean的三种注入方式
开发语言·spring boot·后端·spring
老华带你飞15 小时前
志愿者服务管理|基于springboot 志愿者服务管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
知其然亦知其所以然15 小时前
程序员的最强外挂:用 Spring AI 解锁智谱 AI 画图能力
后端·spring·程序员
利刃大大16 小时前
【SpringBoot】Spring IOC && DI && 五大注解 && Bean && 扫描路径 && 依赖注入
java·spring boot·spring
尤物程序猿16 小时前
spring的监听器的几种使用方式
java·数据库·spring