SpringCloud之网关、服务保护和分布式事务详解

 更新时间:2025年09月17日 15:39:12   作者:l_tian_tian_  
网关负责路由、身份验证及用户ID传递,配置管理支持热更新与动态路由,服务保护通过限流、隔离、熔断防止雪崩,分布式事务采用XA模式强一致性或AT模式高性能但控制复杂

一、网关

网络的关口,负责请求的路由、转发、身份验证

server:
  port: 8080
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.96.129:8848
    gateway:
      routes:
        - id: item-service
          uri: lb://item-service
          predicates:
            - Path=/items/**,/search/**
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/addresses/**,/users/**
        - id: cart-service
          uri: lb://cart-service
          predicates:
            - Path=/carts/**
        - id: trade-service
          uri: lb://trade-service
          predicates:
            - Path=/orders/**
  application:
    name: hm-gateway

二、网关登录校验

自定义过滤器:

package com.hmall.gateway.filters;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        System.out.println("GlobalFilter pre阶段 执行了");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

微服务项目网关:

package com.hmall.gateway.filters;

import com.hmall.common.exception.UnauthorizedException;
import com.hmall.gateway.config.AuthProperties;
import com.hmall.gateway.utils.JwtTool;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;


@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    //不需要处理的请求路径
    public final AuthProperties authProperties;
    public final JwtTool jwtTool;
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //获得请求头
        ServerHttpRequest request = exchange.getRequest();
        //放行不需要拦截的请求
        //路径合法,需要放行
        if (isUnique(request.getPath().toString())){
            //合法,放行
            return chain.filter(exchange);
        }

        //判断令牌是否合法
        String token=null;
        Long userId=null;
        List<String> authorization = request.getHeaders().get("authorization");
        if (authorization != null && authorization.size() > 0) {
            token = authorization.get(0);
        }

        try {
            userId = jwtTool.parseToken(token);

        }
        catch (UnauthorizedException e) {
            //401 未登录、未授权
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        //TODO 保存用户id到请求头,实现多个微服务间用户id的共享
        String userInfo = userId.toString();
        ServerWebExchange swe=exchange.mutate().request(builder -> builder.header("user-info", userInfo)).build();
        //System.out.println(userId);
        //放行
        return chain.filter(swe);
    }

    @Override
    public int getOrder() {
        return 0;
    }


    private boolean isUnique(String path) {
        for (String excludePath : authProperties.getExcludePaths()) {
            if (antPathMatcher.match(excludePath, path)) {
                return true;
            }
        }
        return false;
    }
}
server:
  port: 8080
spring:
  application:
    name: hm-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.96.129:8848
    gateway:
      routes:
        - id: item-service
          uri: lb://item-service
          predicates:
            - Path=/items/**,/search/**
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/addresses/**,/users/**
        - id: cart-service
          uri: lb://cart-service
          predicates:
            - Path=/carts/**
        - id: trade-service
          uri: lb://trade-service
          predicates:
            - Path=/orders/**
        - id: pay-service
          uri: lb://pay-service
          predicates:
            - Path=/pay-orders/**
hm:
  jwt:
    location: classpath:hmall.jks
    alias: hmall
    password: hmall123
    tokenTTL: 30m
  auth:
    excludePaths:
      - /search/**
      - /users/login
      - /items/**
      - /hi

网关传递用户:将用户的id保存在请求头当中,通过统一拦截处理,获取用户的id,放入ThreadLocal当中;请求完成,清理ThreadLocal,实现用户id从网关到各个项目模块的传递

OpenFeign传递用户:OpenFeign中提供了一个拦截器接口,所有由OpenFeign发起的请求都会先调用拦截器处理请求,在拦截处理过程中,我们将ThreadLocal中的用户id放入OpenFeign的请求头当中,其他微服务拦截处理的过程中获得用户id并放入线程当中

三、配置管理

1.拉取共享配置

2.加入相关依赖

        <!--nacos配置管理-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>


        <!--读取bootstrap文件-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>

3.配置热更新

(1)nacos中要有一个与微服务名有关的配置文件

(2)微服务中要以特定方式读取需要热更新的配置属性

package com.hmall.cart.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@Data
@ConfigurationProperties(prefix = "hm.cart")
public class MaxCommodityConfig {
    private Integer maxCommodity;
}

4.动态路由

package com.hmall.gateway.routes;
import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.RequiredArgsConstructor;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Executor;

@Component
@RequiredArgsConstructor
public class DynamicRounterLoader {
    private final NacosConfigManager nacosConfigManager;

    private final RouteDefinitionWriter writer;

    private final String dataId="gateway-routes.json";

    private final String group="DEFAULT_GROUP";

    //记录路由的id
    private HashSet<String> set=new HashSet<String>();


    //在Bean初始化之后执行
    @PostConstruct
    public void initRoutesConfigListener() throws NacosException {

        //拉取配置并更新配置
        String configInfo = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, 5000, new Listener() {

            @Override
            public Executor getExecutor() {
                return null;
            }

            @Override
            public void receiveConfigInfo(String configInfo) {

                //路由表更新,更新监听器
                System.out.println(configInfo+"监听器更新执行了");
                updateRouters(configInfo);

            }
        });


        System.out.println(configInfo+"监听器更新了");
        //第一次启动,更新监听器
        updateRouters(configInfo);





    }

    private void updateRouters(String configInfo) {
        //将json数据转换为实体类
        List<RouteDefinition> routeDefinitionList = JSONUtil.toList(configInfo, RouteDefinition.class);

        //删除原来的路由表
        for (String id : set) {
            writer.delete(Mono.just(id)).subscribe();
        }
        set.clear();
        //添加新的路由表并记录id
        for (RouteDefinition routeDefinition : routeDefinitionList) {
            writer.save(Mono.just(routeDefinition)).subscribe();
            set.add(routeDefinition.getId());
        }

    }


}

将yaml配置转换为json配置:

[
    {
        "id": "item",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}
        }],
        "filters": [],
        "uri": "lb://item-service"
    },
    {
        "id": "cart",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/carts/**"}
        }],
        "filters": [],
        "uri": "lb://cart-service"
    },
    {
        "id": "user",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/users/**", "_genkey_1":"/addresses/**"}
        }],
        "filters": [],
        "uri": "lb://user-service"
    },
    {
        "id": "trade",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/orders/**"}
        }],
        "filters": [],
        "uri": "lb://trade-service"
    },
    {
        "id": "pay",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/pay-orders/**"}
        }],
        "filters": [],
        "uri": "lb://pay-service"
    }
]

三、服务保护和分布式事务

1.雪崩问题

微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用

解决方案:保证代码的健壮性、保证网络的畅通、能应对高并发请求

2.服务保护

请求限流:限制访问服务器的并发量,避免服务因流量激增出现故障

线程隔离:模拟船舱隔板的防水原理。通过限定每个业务能使用的线程数量而将故障业务隔离,避免故障扩散

服务熔断:由断路器统计请求的异常比例或慢调用比例,如果超出阈值则会熔断业务,则拦截该接口请求

3.分布式事务

事务协调者(TC):维护全局和分支事务的状态,协调全局事务提交和回滚

事务管理器(TM):定义全局事务范围、开始全局事务、提交或回滚全局事务

资源管理器(RM):管理分支事务,与TC交谈以注册分支事务和报告分支事务状态

  • XA模式:

优点:事务的强一致性,满足ACID原则​,常用数据库都支持,实现简单,并且没有代码侵入

缺点:因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差​,依赖关系型数据库实现事务​

  • AT模式:

优点:满足ACID原则​,常用数据库都支持,实现简单,并且没有代码侵入,单个RM完成之后进行事务的提交,不占用资源,提高了性能

缺点:难以实现复的事务控制,如特定隔离级别;当事务的隔离级别过低时会出现脏读、不可重复读、幻读问题

​总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java利用Jackson轻松处理JSON序列化与反序列化

    Java利用Jackson轻松处理JSON序列化与反序列化

    Jackson 是 Java 中最流行的 JSON 处理库之一,它提供了许多注解来简化 JSON 的序列化和反序列化过程。这篇文章将介绍一些 Jackson 常用的注解,以帮助您更轻松地处理 JSON 数据
    2023-05-05
  • C#使用MySQLConnectorNet和MySQLDriverCS操作MySQL的方法

    C#使用MySQLConnectorNet和MySQLDriverCS操作MySQL的方法

    这篇文章主要介绍了C#使用MySQLConnectorNet和MySQLDriverCS操作MySQL的方法,相比普通方法能够在Windows下简化很多操作步骤,需要的朋友可以参考下
    2016-04-04
  • Spring Cloud 部署时使用 Kubernetes 作为注册中心和配置中心的方法

    Spring Cloud 部署时使用 Kubernetes 作为注册中心和配置中

    Spring Cloud Kubernetes提供了使用Kubernete本地服务的Spring Cloud通用接口实现,这篇文章主要介绍了Spring Cloud 部署时如何使用 Kubernetes 作为注册中心和配置中心,需要的朋友可以参考下
    2024-05-05
  • SpringBoot集成Druid实现多数据源的两种方式

    SpringBoot集成Druid实现多数据源的两种方式

    这篇文章主要介绍了SpringBoot集成Druid实现多数据源的两种方式,集成com.baomidou的方式和基于AOP手动实现多数据源原生的方式,文中通过代码示例讲解的非常详细,需要的朋友可以参考下
    2024-03-03
  • RabbitMQ核心函数的参数意义和使用场景分析

    RabbitMQ核心函数的参数意义和使用场景分析

    这篇文章主要介绍了RabbitMQ核心函数的参数意义和使用场景分析,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-06-06
  • SpringMVC Controller 返回值的可选类型详解

    SpringMVC Controller 返回值的可选类型详解

    本篇文章主要介绍了SpringMVC Controller 返回值的可选类型详解 ,spring mvc 支持如下的返回方式:ModelAndView, Model, ModelMap, Map,View, String, void,有兴趣的可以了解一下
    2017-05-05
  • IntelliJ IDEA中使用mybatis-generator的示例

    IntelliJ IDEA中使用mybatis-generator的示例

    这篇文章主要介绍了IntelliJ IDEA中使用mybatis-generator,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • Java泛型继承原理与用法详解

    Java泛型继承原理与用法详解

    这篇文章主要介绍了Java泛型继承原理与用法,结合实例形式分析了java泛型继承的相关原理与实现技巧,需要的朋友可以参考下
    2019-07-07
  • Java 抽象类特点总结

    Java 抽象类特点总结

    在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
    2021-10-10
  • JUC三大辅助类CountDownLatch、CyclicBarrier和Semaphore详解

    JUC三大辅助类CountDownLatch、CyclicBarrier和Semaphore详解

    这篇文章主要介绍了JUC三大辅助类CountDownLatch、CyclicBarrier和Semaphore详解,CountDownLatch 类可以设置一个计数器,然后通过 countDown 方法来进行 减 1 的操作,使用 await 方法等待计数器不大于 0,然后继续执行 await 方法 之后的语句,需要的朋友可以参考下
    2024-01-01

最新评论