Spring cloud alibaba——Gateway

网关

微服务之所以存在,就是把一个单体服务拆分成多个服务,可以让多台机器共同协作,提高处理请求的效率,从某种程度上说,微服务可以理解成一个个Service层,所以微服务的url不应该被暴露,从网络安全和服务效率方面来看,微服务需要一个统一的入口去处理,统一调用。

微服务调用容易产生的问题

在这里插入图片描述

解决方案——网关

在这里插入图片描述

常用的网关

  • Zuul:基于Servlet框架搭建,但是延迟比较严重,效率不是很高
  • GateWay:Spring公司研发,底层为Netty的非阻塞异步调用,效率是Zuul的1.6倍,实用功能更多,更易扩展。
  • Nginx+lua:效率更高但是无法融入到微服务中,一般都是单独拿出来用
  • Kong:基于Nginx+lua,不适用未服务。

GateWay

概念

就是一个网关工具,提供一种简单有效的统一的 API 路由管理方式。并提供了网关基本的功能,例如:安全,监控和限流。GateWay官方说明文档

工作流程

GateWay底层使用Netty通讯框架,
工作流程的核心就是路由转发+执行过滤链
在这里插入图片描述

核心概念

  • Route 路由:路由是一个过程,及经过断言的判断,将符合要求的请求转发到对应的微服上,让该微服务处理请求
  • Predicate 断言:断言是配置一些规则,让请求与规则相匹配,如果匹配为真,则可以继续访问,如果匹配为假,则不会路由到微服务
  • Filter 过滤:在路由的过程中,Filter会提供一些功能,例如权限校验、响应内容修改、协议转换等。

可以如下理解:
在这里插入图片描述

搭建过程

一、创建新项目cloud-gateway-9999

二、pom依赖

版本对应规则 https://spring.io/projects/spring-cloud
由于本人使用的boot是2.2.6的,所以使用的gateway版本为Hoxton.SR8

父级项目引入

<properties>
    <spring.cloud-version>Hoxton.SR8</spring.cloud-version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud-version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

子级项目引入

扫描二维码关注公众号,回复: 17027361 查看本文章

注意,由于gateway的依赖和spring-boot-starter-web有冲突,因此需要注意删除依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

三、application.yml文件配置

server:
  port: 9999
spring:
  application:
    name: cloud-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    #开启注册中心路由功能
    gateway:
      discovery:
        locator:
          enabled: true
      # 路由
      routes:
          # 路由id
        - id: nacos-provider
          #  匹配提供服务的路由地址
          uri:  http://localhost:9001/nacos-provider
          predicates: # 断言
            - Path=/demo/** # 断言,路径相匹配进行路由

四、服务提供者Controller

@RestController
@RequestMapping("demo")
public class DemoController {
    
    

    @Value(value = "${server.port}")
    private String serverPort;

    @GetMapping("get")
    public String getServerPort(){
    
    
        return "Hello Nacos Discovery" + serverPort;
    }

}

启动后可以通过gateway服务访问到真正的服务提供者。

五、GatewayConfig通过代码配置

Gateway除了可以使用yml配置文件来配置路由,也可以通过代码方式来进行配置,方法如下:
配置类

package com.cloud.cloudgateway9999.config;


import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {
    
    

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder){
    
    
        // 新建了一个路由规则构建对象,这个对象可以构建多个路由
        RouteLocatorBuilder.Builder routes = builder.routes();
        // ,就会
        /**
         * 如果满足路径http://localhost:9999/demo/customer断言条件,那么
         * 请求就会被转发至http://localhost/9001/nacos-provider/demo/customer
         */
        routes.route("path-custumer",r->r.path("/demo/customer")
                .uri("http://localhost:9001/nacos-provider")).build();
        // 返回路由规则
        return routes.build();
    }
}

访问时会有相同的效果,但是这样配置比较麻烦,建议优先使用yml文件配置。

Gateway负载均衡

自动负载均衡

添加配置:

# 开启自动路由功能,根据服务名称自动创建routes
    gateway:
      discovery:
        locator:
          enabled: true

关于这个配置:默认为关闭状态,如果这个配置为开启状态,则说明:gateway与服务可以通过serviceId完成默认转发,如果多个服务集群的话,可以自定进行负载均衡在这里插入图片描述

对比nacos自带的负载均衡Dubbo,在启动类配置

@Bean
// @Bean注解表示将restTemplate装配到容器中来
@LoadBalanced
// 负载均衡,表示这个restTemplate在调用服务提供者接口时经过负载均衡
public RestTemplate restTemplate(){
    
    
    return new RestTemplate();
}

就可以实现,而nacos可以直接通过discovery.locator.enabled开关来做到。

discovery.locator.enabled开关可以根据serviceId自动转发,但是会导致服务名称暴露,因此,实际应用中不会打开该开关。

手动负载均衡

可以在yml配置负载均衡的规则:

# 路由
routes:
    # 路由id
  - id: routeNo1
    #  负载均衡:如果符合断言,则按照服务名为nacos-provider的服务进行负载均衡访问,并且,例如,访问gateway的请求地址为:http://9999/demo/get,会被路由成http://localhost:9001/demo/get和http:/9002/demo/get
    uri: lb://nacos-provider
    predicates:
      - Path=/demo/** # 断言,路径相匹配进行路由

在这里插入图片描述
效果如下:
在这里插入图片描述

GateWay断言

断言是指一种匹配规则,可以对路径、请求头、请求发送的时间等属性进行匹配,断言通过以后,才能够被转发到其他微服务。GateWay断言官方说明文档

断言的种类

  1. After:匹配在指定日期时间之后发生的请求。
  2. Before:匹配在指定日期之前发生的请求。
  3. Between:需要指定两个日期参数,设定一个时间区间,匹配此时间区间内的请求。
  4. Cookie:需要指定两个参数,分别为name和regexp(正则表达式),也可以理解Key和Value,匹配具有给定名称且其值与正则表达式匹配的Cookie。
  5. Header:需要两个参数header和regexp(正则表达式),也可以理解为Key和Value,匹配请求携带信息。
  6. Host:匹配当前请求是否来自于设置的主机。
  7. Method:可以设置一个或多个参数,匹配HTTP请求,比如GET、POST
  8. Path:匹配指定路径下的请求,可以是多个用逗号分隔
  9. Query:需要指定一个或者多个参数,一个必须参数和一个可选的正则表达式,匹配请求中是否包含第一个参数,如果有两个参数,则匹配请求中第一个参数的值是否符合正则表达式。
  10. RemoteAddr:匹配指定IP或IP段,符合条件转发。
  11. Weight:需要两个参数group和weight(int),实现了路由权重功能,按照路由权重选择同一个分组中的路由。

使用示例

 routes:
	   # 路由id
	  - id: routeNo1
	    uri: lb://nacos-provider
	    predicates:
	      routes:
          # 路由id
        - id: routeNo1
          #  匹配提供服务的路由地址
	      - Path=/demo/** 
	      # 必须在该时间之后发起的请求才能被处理
	      - After=2022-07-18T19:18:44.635+08:00[Asia/Shanghai]
	      # 匹配Cookie的key和value(正则表达式),cookie中一定要有一key为username的键值对,并且值必须匹配正则表达式,此处必须为字母,不能为数字
	      - Cookie=username,[a-z]+ 
	      # 匹配请求头,请求头必须含有X-Request-Id,并且必须是数字
	      - Header=X-Request-Id,\d+ 
	      # 匹配当前的主机地址发出的请求host
	      - Host=**.cloud.com
	      # 可以设置一个或多个参数,匹配HTTP请求,比如GET、POST
	      - Method=GET,POST
	      # 匹配请求参数,这里如果需要匹配多个参数,可以写多个Query
	      - Query=id,.+ 

时间类的标签必须是ZonedDateTime类型,可以参考以下代码获取具体写法:

 public static void main(String[] args){
    
    
     ZonedDateTime time = ZonedDateTime.now();
     System.out.println(time);
 }

weight规则如下:

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

该路由会将约 80% 的流量转发到weighthigh.org,将约 20% 的流量到weightlow.org。

GateWay过滤器Filter

GateWay内置的Filter有两种生命周期,一种是pre,作用于业务逻辑之前,一种是post,作用于业务逻辑之后;其中,Gateway自带的Filter有两种,一种是GateWayFilter,单一过滤器,有32种,一种是GlobalFilter,全局过滤器,由9种,详见GateWay过滤器官网

StripPrefix过滤器

如果在9001的配置文件中配置了context-path,那么,按照原来的地址就访问不到服务了,而是需要在原地址上拼接一部分内容。

server:
  port:
    9001
  servlet:
    context-path: /nacos-provider

例如,原来访问http://localhost:9999/demo/get就会转发成http://9001/demo/get,但是现在,访问的地址变为http://localhost://9001/nacos-provider/demo/get,如此这样的话就访问不到资源了。

StripPrefix过滤器的作用就是给访问路径去除一部分

  # 路由
   routes:
       # 路由id
     - id: routeNo1
       #  匹配提供服务的路由地址
       uri: lb://nacos-provider
       predicates:
         - Path=/demo/** # 路径相匹配进行路由
         # 必须在该时间之后发起的请求才能被处理
         - After=2022-07-18T19:18:44.635+08:00[Asia/Shanghai]
       filters:
         # 去掉uri的第一部分
         - StripPrefix=1      

访问路径为:http://localhost:9999/demo/nacos-provider/demo/get
转换为:http://localhost:9001/nacos-provider/demo/get
就能顺利访问到资源了

自定义过滤器

一、新建MyFilter
package com.cloud.cloudgateway9999.filter;

import com.alibaba.csp.sentinel.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
@Slf4j
public class MyFilter implements Ordered,GlobalFilter {
    
    

    /**
     *
     * @param exchange 可以拿到一个方法的请求和响应
     * @param chain 过滤器链
     * @return 是否放行
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        // 获取请求里请求参数,第一个username的值
        String username = exchange.getRequest().getQueryParams().getFirst("username");
        log.info("---------进入GateWay过滤器-------------");
        if(StringUtil.isEmpty(username)){
    
    
            log.info("---------非法用户名,请求被拒绝-------------");
            //返回状态码为406,不可接受的请求
            exchange.getResponse().setRawStatusCode(HttpStatus.SC_NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 加载过滤器的顺序
     * @return 返回一个整数,数字越小优先级越高
     */
    @Override
    public int getOrder() {
    
    
        return 0;
    }
}

二、测试访问生效

不携带过滤器设置的请求参数,请求失败
在这里插入图片描述

携带过滤器设置的请求参数,访问成功
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44712778/article/details/125789566