高级框架第十二天Gateway:

Gateway:网关

主要内容

1.API网关

2.Spring Cloud Gateway介绍

3.Gateway入门案例

4.谓词

5.Filter

6.使用Gateway实现服务降级

一.API网关

1.什么是API网关

API网关作用就是把各个服务对外提供的API汇聚起来,让外界看起来是一个统一的接口.同时也乐意在网关中提供额外的功能

总结:网关就是所有项目的一个统一接口

2.网关组成

网关=路由转发+过滤器(编写额外功能)

2.1路由转发

接收外界请求,通过网关的路由转发,转发到后端的服务上

如果只有这一个功能看起来和之前学习的Nginx反向代理服务器很像,外界访问nginx,由nginx做负载均衡,后把请求转发到对应服务器上

2.2过滤器

网关非常重要的功能就是过滤器

过滤器中默认提供了2s内置工鞥呢还自持额外的自定义功能

对于我们来说比较常用的功能有网关的容错,限流以及请求及相应的额外处理

3.Spring Cloud中提供的网关解决方案

3.1Spring Cloud Netflix Zuul

属于Spring Cloud Netflix下一个组件,具有灵活,简单的特点.在早期Spring Cloud中使用的比较多.

其版本更新都依赖与Netflix Zuul

3.2Spring Cloud Gateway

由Spring自己推出的网关产品,完全依赖Spring自家产品.符合Spring战略意义,器更新版本等都由Spring自己把控

目前很多项目中都是使用Gateway替代Zuul

在本套课程中讲解的也是Gateway

二.Spring Cloud Gateway介绍

1.简介

Spring Cloud Gateway是Spring Cloud的二级子项目,提供了微服务网关功能,包含:权限安全,监控/指标等功能

2.名词解释

在学习Gateway时里面有一些名词需要提前了解,这对于后面的学习是很有帮助的

2.1Route

Route中文称为路由,Gateway里面的Route是主要学习内容,一个Gateway项目可以包含多个Route

一个路由包含ID,URI,Predicate集合,Filter集合

在Route中ID是自定义的,URL就是一个地址.剩下的Predicate和Filter学习明白了,Route就学习清楚了.

2.2Predicate

中文:谓词

谓词是学习Gateway比较重要的一点,简单点理解谓词就是一些附加条件和内容

2.3Filter

所有生效得Filter都是Gateway的实例.在Gateway运行过程中Fileter负责在代理服务"之前"或"之后"去做一些事情

2.4流程

2.4.1流程文字解释

网关客户端访问Gateway网关,Gateway中Handler Mapping对请求URL进行处理.处理完成后交换Web Handler,Web Handler会被Filter进行过滤.Filter中前半部分代码是处理请求的代码.处理完成后调用真实被代理的服务.被代理服务响应结果,结果会被Filter中后半部分代码进行操作,操作完成后把结果返回给Web Handler,在返回给Handler Mapping,最终响应给客户端

三.Gateway入门案例

1.准备内容

1.1Eureka Server

因为Gateway依赖Eureka,需要Eureka中获取真实代理项目地址后,在进行访问.所以需要准备一个Eureka Server(实例中是非集群Eureka),端口设置为8761

2.新建项目DemoOne

一个普通的Eureka Client项目,重点关注应用程序名和控制器即可,其他代码省略

2.1配置文件

spring:

    application:

        name:demo-one

2.2控制器

@Controller

public class DemoController{

    @RequestMapping("/one")

    @ResponseBody

    public String demo(){

        System.out.println("执行one控制器");

        return "one";

    }

    @RequestMapping("/demo/one")

    @ResponseBody

    public String demo2(){

        System.out.println("执行demo-one控制器");

        return "demo-one";

    }

}

3.新建项目GatewayDemo

3.1编写pom.xml

    org.springframework.boot

    spring-boot-starter-parent

    2.2.5.RELEASE

   

       

            org.springframework.cloud

            spring-cloud-dependencies

            Hoxton.SR3

            pom

            import

        

    

   

        org.springframework.cloud

        spring-cloud-starter-gateway

    

   

        org.springframework.cloud

        spring-cloud-starter-netflix-eureka-client

    

3.2新建配置文件

谓词中Path一般会给每个项目前面起一个无意义的别名

如Path=/abc/**当看到abc是知道路由转发到项目abc,后面的内容才是真实要访问abc的内容

如Path=/jqk/**表示需要转发到jql项目

要明白:uri中第一部分就是为了防止各个项目中出现重名路径导致路由转发失败

server:

    port:9000

spring:

    application:

        name:gateway-demo

    cloud:

        gateway:

            discovery:

                locator:

                    enabled: true # 开启当前项目服务注册于发现功能

                    lower-case-service-id:true # 把服务名称转换为小写,Eureka中默认都是大写

                - id:demo    # 自定义唯一标识,只要不重复即可

                    url:lb://EUREKA-CLIENT # Lb:LoadBalance EUREKA-CLIENT代理项目的名

                    predicates:Path=/demo/** # 路径规则

                    filters: StripPrefix=1 # 转发后忽略第一层

3.新建启动器

新建com.bjsxt.GatewayApplication

@SpringBootApplication

public class GatewayApplication(){

    public static void main(String[] args){

        SpringApplication.run(GatewayApplication.class,args);

    }

}

4.访问测试

访问GatewayDemo项目

http://localhost:9000/demo/one

页面会显示

通过喝过可以看出,用户虽然访问的GatewayDemo网关项目,但是最后真是访问的是DemoOne项目中/one控制器

5.修改配置参数

修改GatewayDemo项目中配置文件的路由参数,把StripPrefix的值由1改成0后,重启项目

filters:StripPrefix=0

再次访问http://localhost:9000/demo/one会发现

四.谓词

谓词:当满足条件在进行路由转发

在Spring Cloud Gateway中谓词实现GatewayPredicate接口.其中类名符合:XXXRoutepredicateFactory,其中XXX就是在配置文件中谓词名称.在上面实例中Path=/demo/**实际上使用的就是PathRoutePredicateFactory

所有的谓词都设置在predicates属性中,当设置多个谓词时区逻辑与条件,且一个谓词只能设置一组条件,如果需要有多个条件,添加多个相同谓词

1.Query

1.1.设置必须包含的参数名

下面两种写法等效.都表示路径满足/demo/**同时包含参数abc

Path和Query是谓词.abc是请求参数名称

在浏览器中输入:http://localhost:9000/demo/one?abc=jdk

predicates:Path=/demo/**,Query=abc

predicates:

    -Path=/demo/**

    - Query=abc

1.2设置参数的值

abc请求参数名称.jdk是abc的值,是一个正则表达式.在正则表达式中点(.)表示匹配任意一个字符.所以当请求参数abc=jqka或abc=jqkw能满足谓词条件

在谓词中赋值使用逗号(,)赋值因为Qurey后面已经有等号,在值内容值在出现等号语法说不过去了

predicates:

    - Path=/demo/**

    - Query=abc,jqp.

2.Header

表示请求头中必须包含的内容

注意:

    参数名和参数值主键依然使用逗号

    参数值要使用正则表达式

如果Header只有一个值表示请求头中必须包含的参数.如果有两个值,第一个表示请求头必须包含的参数名,第二个表示请求头参数的对应值

predicates:

    - Path=/demo/**

    - Query=abc,jqk+

    - Header=Connection,keep-alive

如果设定请求头中需要包含多个参数设置.设置多个Header.在此处演示多个相同谓词配置,其他谓词中就不在强调可以配置多个谓词

predicates:

    - Path=/demo/**

    - Query=abc,jqk+

    - Header=Connection,keep-alive

    - Header=Cache-Control,max-age=0

3.Method

Method表示请求方式.支持多个值,使用逗号分隔,多个值之间为or条件

predicates:

    - Path=/demo/**

    - Method=GET,POST

4.RemoteAddr

允许访问的客户端地址

要注意使用127.0.0.1而不要使用localhost

predicates:

    - Path=/demo/**

    - RemoteAddr=127.0.0.1

5.Host

匹配请求参数中Host参数的值.满足Ant模式(之前在Spring Security中学习过)可以使用

    ?匹配一个字符

    #匹配0个或多个字符

    ##匹配0个或多个目录

predicates:

    - Path=/demo/**

    - Host=127.0.0.1:9000

6.Cookie

要求请求中包含指定Cookie名和满足特定正则要求的值

Cookie必须有两个值,第一个Cookie包含的参数名,第二个表示参数对应的值,正则表达式.不支持一个参数写法

predicates:

    - Path=/demo/**

    - Cookie=age,.*

7.Before

在指定时间点之前

predicates:

- Path=/demo/**

- Before=2020-01-31T18:00:00.000+08:00[Asia/Shanghai]

8.After

在指定时间之前

predicates:

- Path=/demo/**

- After=2020-01-31T18:00:00.000+08:00[Asia/Shanghai]

9.Between

请求时必须在设定的时间范围内,才进行路由转发

注意:时间的格式

predicates:

    - Path=/demo/**

    - Between=2020-01-31T18:00:00.000+08:00[Asia/Shanghai],2020-02-01T00:00:00.000+08:00[Asia/Shanghai]

10.Weight

负载均衡中权重.同一个组中URI进行负载均衡

语法:Weight=组名,负载均衡权重

在Eureka中注册两个服务,这个服务(项目)是相同的,应用程序名分别叫做demo-one和demo-two

Gateway在路由匹配时demo-one将占20%,demo-two将占80%

routes:

    - id:xiaoming

        uri: lb://demo-one

        prdicates:

            - Path=/demo/**

            - Weight=group,2

        filters: StripPrefix=1

    - id: xiaoming2

        uri: lb://demo-two

        predicates:

            - Path=/demo/**

            - Weight=group,8

        filters: StripPrefix=1

五.Filter

Filter作用:在路由转发到代理服务器之前和代理服务返回结果之后额外做的事情.Filter执行了说明谓词条件通过了

在Spring Cloud Gateway的路由中Filter分为:

    内置Filter:都是GatewayFilter实现类

    自定义GlobalFilter

1.内置Filter

之前使用StripPrefix就是内置Filter,所有内置Filter都实现GatewayFilter接口.使用时filters属性中过滤器名为XXXGatewayFilterFactory的类对应的名称为XXX.

当过滤器包含参数

下面所有Filter基本都是支持单个值,如果需要设置多个,需要写多个对应Filter

1.1.AddRequestHeader

添加请求头参数,参数和值之间使用逗号分隔

filters:

    - StripPrefix=1

    - AddRequestHeader=MyHeader,jqk

1.2AddRequestParameter

添加请求表单参数,多个参数需要有多个过滤器

filters:

    - StripPrefix=1

    - AddRequestParameter=name,bjsxt

    - AddRequestParameter=age,123

1.3AddResponseHeader

添加响应头

1.4DedupeResponseHeader

对指定响应头去重复

语法:DedupeResponseHeader=响应头参数 响应头参数strategy

可选参数strategy可取值:

    RETAIN_FIRST默认值,保留第一个

    RETAIN_LAST保留最后一个

    RETAIN_UNIQUE保留唯一的,出现重复的属性值,会保留一个.例如有两个My:bbb的属性,最后会只留一个

- DedupeResponseHeader=My Content-Type,RETAIN_UNIQUE

1.5CircuitBreaker

实现熔断时使用,支持CircuitBreaker和Hystrix两种

1.6FallbackHeaders

可以添加降级时的异常信息

1.7PrefixPath

匹配所有前缀满足条件的URI

1.8RequestRateLimiter

限流过滤器

1.9RedirectTo

重定向.有两个参数,status和url.其中status应该300系列重定向状态码

1.10RemoveRequestHeader

删除请求头参数

1.11RemoveResponseHeader

删除响应头参数

1.13RewritePath

重写请求路径

1.14RewriteResponseHeader

重写响应头参数

1.15SaveSession

如果项目中使用SpringSecurity和SpringSession整合时,此属性特别重要

1.16SecureHeaders

具有权限验证时,建议的头信息内容

1.17SetPath

功能和StripPrefix有点类似.语法更贴近restful

当前请求路径为/red/blue时会将/blue发送给下游

spring:

    cloud:

        gateway:

            routes:

                - id:setpath_route

                uri:https://example.org

                predicates:

                    - Path=/red/{segment}

                filters:

                    - SetPath=/{segment}

1.18SetRequestHeader

替换请求参数头.不是添加

1.19SetResponseheader

替换响应头参数

1.20SetStatus

设置响应状态码

1.21StripPrefix

跳过路由uri中前几段后发送给下游

1.22Retry

设置重试次数

1.23RequestSize

请求最大大小.包含maxSize参数,可以有单位"KB"或"MB"默认为"B"

1.24ModifyRequestBody

修改请求体内容

1.25ModifyResponseBody

修改响应体

六.使用Gateway实现限流

可以利用Gateway中RequestRateLimiter实现限流

1.常见的限流算法

1.1计数器算法

以QPS(每秒查询率Queries-per-second)为100举例

从第一个请求开始计时.每个请求让计数器加一.当达到100以后,其他的请求都拒绝.如果1秒钟内前200ms请求数量已经达到了100,后面800ms中500次请求都被拒绝了,这种情况称为"突刺现象"

1.2漏桶算法

漏桶算法可以解决突刺现象

和生活中漏桶一样,有一个水桶,下面有一个"露眼"往出漏水,不管桶里有多少水,漏水的速率都是一样的.但是既然是一个桶,桶里装的水都是有上限的.当到达了上限新进来的首就装不了(主要出现在突然进来大量水的情况)

1.3令牌桶算法

令牌桶算法可以说是对漏桶算法的一种改进

在桶中放令牌,请求获取令牌后才能继续执行.如果桶中没有令牌,请求可以选择进行等待或者直接拒绝

由于桶中令牌是按照一定速率放置的,所以可以一定程度解决突发访问.如果桶中令牌最多有100个,qps最大为100

2.Gateway中限流

RequestRateLimiter是基于Redis和Lua脚本实现的令牌桶算法

2.1添加依赖

    org.springframework.boot

    spring-boot-starter-parent

    2.2.5.RELEASE

   

       

            org.springframework.cloud

            sprng-cloud-dependencies

            Hoxton.SR3

            pom

            import

        

    

   

        org.springframework.boot

        spring-cloud-starter-gateway

    

   

        org.springframework.cloud    

        spring-cloud-startburstCapacity:er-netflix-eureka-client

    

2.2编写配置文件

新建配置文件application.yml

参数说明:

    key-resolver:使用SqEL获取Spring容器中实例

    replenishRate:每秒放入令牌的数量

    burstCapacity:容量

server:

    port:9000

spring:

    application:

        name:gateway-demo

    cloud:

        gateway:

            discovery:

                locator:

                    enabled: true # 开启当前项目服务注册与发现功能

                    lower-case-service-id:true # 把服务名转换为小写,Eureka中默认都是大写

            routes;

                - id: demo # 自定义唯一标识,只要不重复即可

                    uri:lb://application-client # Lb:LoadBalance EUREKA-CLIENT 代理项目的名

                    predicates:Path=/abc # 路径规则

                    filters: # 转发后忽略第一层

                        - name: RequestRateLimiter

                            args:

                                key-resolver: '#{@abcComponet}'

                                redis-rate-limiter.replenishRate:1

                                redis-rate-limiter.burstCapacity:3

redis:

    host:192.168.232.132

2.3新建实例

新建com.bsjxt.component.AbcComponet

just()方法参数表示根据什么进行限流.实例中是根据主机名进行限流.参数值影响Redis中key中红色部分

@Component

public class AbcComponet implements KeyResolver{

    @Override

    public Monoresolve(ServerWebExchange exchange){

        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());

    }

}

2.4新建启动类

@SpringBootApplication

public class AbcApplication{

    public static void main(String[] args){

        SpringApplication.run(AbcApplication.class,args);

    }

}

2.5使用JMeter测试

会发现3个请求时成功的,后面很多失败的,1秒后的请求时成功了

七.使用Gateway实现服务降级

Spring Cloud Gateway可以利用Hystrix实现服务降级等功能

当Gateway进行路由转发时,如果发现下游服务连接超时允许进行服务降级

实现原理:当连接超时时,使用Gateway自己的一个降级接口返回托底数据,保证程序继续运行

1.添加依赖

在GatewayDemo中额外添加

    org.springframework.cloud

    spring-cloud-starter-netflix-hystrix

2.新建控制器

新建com.bjsxt.controller.MyFailbackController添加降级控制器方法,方法中根据需求编写自己的逻辑

@Controller

public class MyFallbackController{

    @RequestMapping("/myfallback")

    @ResponseBody

    public String fallback(){

        return "托底数据";

    }

}

3.修改配置文件

在配置文件中filters属性添加

其中args.name取值是任意的,最终会被设置为Hystrix的commandKey.但是不能省略,省略会导致prg.springframework.cloud.gateway.filter.factory.HystrixGatewayFilterFactory.Config的Setter为null,因为没有设置Hystrix的commandKey等内容时就没有执行Setter的构造方法

routes:

    - id:demo

    uri:lb://EUREKA-CLIENT

    predicates:

        - Path=/demo/**

filters:

    - StripPrefix=1

    - name: Hystrix

        args:

            name: fallbackcmd

            fallbackUri: forward:/myfallback

八.GlobalFilter

全局过滤器不需要工厂,也不需要配置,直接对所有的路由都生效

可以使用GlobalFilter实现统一的权限验证,日志记录等希望对所有代理的项目都生效的内容都可以配置在全局过滤器中

且在项目中可以配置多个GlobalFilter的实现类.都可以自动执行

@Component

public class MyGlobalFilter implements GlobalFilter{

    private Logger = LoggerFactory.getLogger(MyGlobalFilter.class);

    @Override

    public Monofilter(ServerWebExchange exchange,GatewayFilterChain chain){

        logger.info("执行全局过滤器-logger");

        return chain.filter(exchange);

    }

}

九.自定义FilterFactory

可以定义针对于Router的Filter

注意:

    1.类名必须叫做XXXGatewayFilterFactory注入到Spring容器后使用时的名称就叫做XXX.

    2.类必须继承AbstractGatewayFilterFactory

    3.所有需要传递进来的参数都配置到当前类的内部类Config中

1.新建类

@Component

public class MyRouteGatewayFilterFactory extends AbstractGatewayFilterFactory{

    public MyRouteGatewayFilterFactory(){

        super(MyRouteGatewayFilterFactory.Config.class);

    }

    @Override

    public GatewayFilter apply(Config config){

        return new GatewayFilter(){

            @Override

            public Monofilter(ServerWebExchange exchange,GatewayFilterChain chain){

                System.out.println("在这个位置鞋垫东西传递过来的name:+"+config.getName()+",age:"+config.getAge());

                return chain.filter(exchange);

            }

            @Override

            public String toString(){

                return GatewayToStringStyler.filterToStringCreator(MyRouteGatewayFilterFactory.this).append("name",config.getName()).toString();

            }

        };

    }

    @Override

    public ListshortcutFieldOrder(){

        return Arrays.asList("name");

    }

    public static class Config(){

        private String name;

        private int age;

        public int getAge(){

            return age;

        }

        public void setAge(int age){

            this.age=age;

        }

        public Config(){}

        public String getName(){

            return name;

        }

        public void setName(String name){

            this.name=name;

        }

    }

}

2.在配置文件中配置

filters.name:过滤器的名称

filters.args.name为MyRouteGatewayFilterFactory.Config配置的属性

routes:

    - id: bjsxt

    uri: lb://JQK

    predicates:

        - Path=/project1/**

    filters:

        - StripPrefix=1

        - name: MyRoute

            args:

                name: hello

                age: 12

你可能感兴趣的:(高级框架第十二天Gateway:)