SpringClound——网关、服务保护和分布式事务

一、网关

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

XML 复制代码
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

二、网关登录校验

自定义过滤器:

TypeScript 复制代码
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;
    }
}

微服务项目网关:

java 复制代码
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;
    }
}
XML 复制代码
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.加入相关依赖

java 复制代码
        <!--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)微服务中要以特定方式读取需要热更新的配置属性

java 复制代码
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.动态路由

java 复制代码
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配置:

html 复制代码
[
    {
        "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完成之后进行事务的提交,不占用资源,提高了性能

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

相关推荐
橙子家5 小时前
浏览器缓存之【基础键值存储】:Local storage 和 Session storage
前端
星星在线8 小时前
MusicFree:一个「All in One」的个人音乐服务器,让听歌回归简单
前端·后端
IT_陈寒9 小时前
Redis的SETNX并发问题让我加了三天班
前端·人工智能·后端
demo007x9 小时前
Docling 文档转换以及技术架构分析
前端·后端·程序员
京东云开发者10 小时前
京东市民服务又“上新”!这次是黑龙江“龙易办”
前端
袋鱼不重10 小时前
我的神奇同事,AI 用多了居然写了个 Open In Codex
前端·后端·ai编程
大树8810 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠10 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
Fireworks11 小时前
深入vue3源码解读 -- 1、响应式的基础概念
前端
程序员黑豆11 小时前
JDK 下载安装与配置详细教程
java·前端·ai编程