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完成之后进行事务的提交,不占用资源,提高了性能

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

相关推荐
我一定会有钱1 小时前
Linux爆音问题解决方法(隔一会会有奇怪噪音)
linux·运维·服务器
奋斗的小羊羊2 小时前
HTML5关键知识点之多种视频编码工具的使用方法
前端·音视频·html5
前端呆猿2 小时前
深入解析HTML5中的object-fit属性
前端·css·html5
再学一点就睡2 小时前
实现大文件上传全流程详解(补偿版本)
前端·javascript·面试
Dobby_054 小时前
【Ansible】变量与敏感数据管理:Vault加密与Facts采集详解
linux·运维·云原生·ansible
你的人类朋友4 小时前
【Node&Vue】什么是ECMAScript?
前端·javascript·后端
路灯下的光4 小时前
用scss设计一下系统主题有什么方案吗
前端·css·scss
zcz16071278214 小时前
服务器与客户端
运维·服务器
准女婿_4 小时前
优考试局域网系统V6.0.0版
linux·windows·用户运营