• springcloud gateway网关浅析


    网关是微服务架构中非常重要的一个组件,在微服务应用中,客户端所有的请求都是先经过网关,然后再转发到具体的微服务上,客户端无需知道具体微服务的地址,知道网关的地址即可。下面对Spring Cloud GateWay网关进行简单的聊聊。

    1、单体架构
    在传统的springboot单体应用中,一般只有一个后端服务,如下
    在这里插入图片描述
    2、微服务架构
    在springcloud微服务架构中,往往有多个微服务,这些微服务可能部署在不同的机器上,而且一个微服务可能会扩容成多个相同的微服务,组成微服务集群。
    在这里插入图片描述
    上面的情况下,会出现如下问题

    • 添加鉴权:需要对每个微服务进行改造;
    • 流量控制:需要对每个微服务进行改造;
    • 跨域问题:需要对每个微服务进行改造;
    • 安全问题:每个微服务需要暴露自己的Endpoint (ip+端口)给客户端;

    痛点其实就是各个微服务都有一个入口,没有统一的入口,这时在客户端和服务器之间增加一个节点作为唯一的入口,这个就是网关。
    多个微服务之间若需要进行通信,还需要用到远程调用组件了,如OpenFeign。
    在这里插入图片描述
    3、网关
    应用场景,有三个节点:网关、客户端、服务端,其通信对话如下
    网关:客户端你好,你可以访问我了,我可以将你想发给微服务的流量进行转发,微服务处理后再将结果饭给你;
    客户端:网关你好,你没有赚差价吧?
    网关:客户端你好,我可能会加些请求头,做下认证、鉴权、限流;
    客户端:网关你好,微服务自己可以做吧?
    网关:客户端你好,每个微服务都自己加,很麻烦了,也不统一呀;
    服务端:网关你好,你会为微服务保密地址吗?
    网关:服务端你好,当然会保密了,客户端只知道我网关的地址,不需要知道微服务的地址,只需要知道路由就行,剩下的交给我来转发;

    4、Spring Cloud Gateway网关
    gateway的工作流程如下
    在这里插入图片描述
    客户端的请求到达网关后,先经过断言Predicate判断,符合那个路由规则,转换映射后端的某个服务;然后经过过滤器链,对请求进行拦截,如添加请求头,参数校验等,最后再将请求转发到实际的后端服务;后端服务处理完成之后,将结果返回给网关,网关再次进行过滤,然后将结果返回给客户端。
    断言配置的规则示例如下

    spring:
      cloud:
        gateway:
          routes:
            - id: route_order # 订单微服务路由规则
              uri: lb://order # 负载均衡,将请求转发到注册中心注册的order 服务
              predicates: # 断言
                - Path=/api/order/** # 如果前端请求路径包含 api/order,则应用这条路由规则
              filters: #过滤器
                - RewritePath=/api/(?<segment>.*),/$\{segment} # 将跳转路径中包含的api替换成空
    
            - id: route_member # 会员微服务路由规则
              uri: lb://member # 负载均衡,将请求转发到注册中心注册的 member 服务
              predicates: # 断言
                - Path=/api/member/** # 如果前端请求路径包含 api/member,则应用这条路由规则
              filters: #过滤器
                - RewritePath=/api/(?<segment>.*),/$\{segment} # 将跳转路径中包含的 api 替换成空
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    其中uri: lb://member,标识将请求转发给member微服务,且支持负载均衡,lb是loadbalance的缩写。

    5、Spring Cloud Gateway动态路由
    微服务架构中,不会直接通过IP+端口的方式来访问微服务,而是通过服务名的方式来访问,这时引入了注册中心,多个微服务将自己注册到注册中心,这样注册中心就保存了微服务与IP+端口的映射信息。
    在这里插入图片描述
    动态路由就算添加一个微服务或者IP地址变了,网关都可以感知到,但是配置是不需要更新的,微服务的集群个数、IP和端口是动态可变的。
    那么此时整合各个组件之后,整体的架构中请求的转发如下
    在这里插入图片描述

    客户端先将请求发送给Nginx,然后转发给网关,网关经过断言匹配到一个路由后,将请求进行过滤然后转发给知道uri,这个uri可以配置成微服务的名字,gateway网关从注册中心拉取注册表,就能知道服务名对应的IP+端口,若一个微服务部署了多台机器,则通过负载均衡进行请求的转发。

    6、Spring Cloud Gateway过滤
    gateway中的过滤器就是统一对请求和响应进行一些过滤操作,安装请求和响应分为两种:Pre类型和Post类型。

    • Pre 类型:在请求被转发到微服务之前,对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等操作。
    • Post 类型:微服务处理完请求后,返回响应给网关,网关可以再次进行处理,例如修改响应内容或响应头、日志输出、流量监控等。

    按作用范围也可以分为:全局过滤器和局部过滤器。

    • GlobalFilter:全局过滤器,应用在所有路由上的过滤器;
    • GatewayFilter:局部过滤器,应用在单个路由或一组路由上的过滤器。标红色表示比较常用的过滤器;

    gateway网关也自带了很多过滤器,详细查看官网。
    在gateway网关中做一个自定义的登录认证过滤器,如客户端登录时,将用户名和密码发送给网关,网关转发给认证服务器,如果账号密码正确,则拿到一个JWT token,然后客户端再访问应用服务时,先将请求发送给网关,网关统一做token认证,如果token符合条件,再将请求转发给应用服务,流程如下
    在这里插入图片描述
    实例
    过滤器代码

    @Component
    public class AuthGlobalFilter implements GlobalFilter, Ordered {
    
        private AntPathMatcher antPathMatcher = new AntPathMatcher();
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();
            String token = request.getHeaders().getFirst("token");
            if(!antPathMatcher.match("/login", path)) {
                if(StringUtils.isEmpty(token) || !"admin".equals(token)) {
                    ServerHttpResponse response = exchange.getResponse();
                    return out(response);
                }
            }
            //内部服务接口,不允许外部访问
            if(antPathMatcher.match("/**/inner/**", path)) {
                ServerHttpResponse response = exchange.getResponse();
                return out(response);
            }
            return chain.filter(exchange);
        }
    
        @Override
        public int getOrder() {
            return 0;
        }
    
        private Mono<Void> out(ServerHttpResponse response) {
            String message = "口令失效.";
            byte[] bits = message.getBytes(StandardCharsets.UTF_8);
            DataBuffer buffer = response.bufferFactory().wrap(bits);
            //指定编码,否则在浏览器中会中文乱码
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
            return response.writeWith(Mono.just(buffer));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    未携带token
    在这里插入图片描述
    携带token
    在这里插入图片描述

  • 相关阅读:
    RabbitMQ入门指南
    CSS基础入门手册
    Python爬虫遇上动态加载
    将json数据导入到ES集群——解决方案对比&填坑日记
    数据结构——AVL树(详解 + C++模拟实现)
    RichView TRVStyle
    2022年最新宁夏水利水电施工安全员考试题库及答案
    hfctf_2020_marksman(劫持exit_hook或dlopen)
    如何在30分钟完成表格增删改查的前后端框架搭建
    快速上手prometheaus grafana 监控
  • 原文地址:https://blog.csdn.net/leijie0322/article/details/126530540