SpringCloud之Gateway使用篇

1.环境准备

本篇文章我们将实现下图所示的架构
在这里插入图片描述
我们可以接着使用前面《SpringCloud之Feign使用篇》中的项目环境,当然也也可以自己重新搭建都是可以的,这里我们使用的springcloud的版本是:Greenwich.RELEASE

  1. 注册中心我们使用Eureka ,为两个实例,9090与9091
  2. 用户服务 spring-cloud-user-service-consumer ,也是两个实例,8080与8081
  3. 订单服务 spring-cloud-order-service-provider ,也是两个实例,7070与7071
  4. 服务网关使用的是springcloud gateway组件 端口是8020
  5. 再就是nginx作为我们项目入口,为80端口

2.简单使用

2.1 网关项目的搭建

新建module spring-cloud-gateway-server

2.1.1 依赖

需要我们在spring-cloud-gateway-server 项目pom.xml添加依赖,这里 父工程我没有使用之前的,而是选择了spring-boot,因为在springcloud gateway中,web使用的webflux 没有使用spring mvc ,而之前的父工程中引入mvc。

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>
    <dependencyManagement>
        <!--spring cloud依赖版本管理-->
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
        </dependency>
        <!--eureka 客户端依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--gateway 依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <!--webflux 依赖 ,gateway 使用的是webflux,并没有使用springmvc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <!--springboot监控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
2.1.2 配置

在网关服务 application.yml 中配置网关路由规则

server:
  port: 8020
spring:
  application:
    name: spring-cloud-gateway-server
  cloud:
    gateway:

      # 配置路由,可以配置多个
      routes:

        - id: order-microservice-router  # id 自定义路由的id
          uri: http://127.0.0.1:7070   # uri就是 目标服务地址
          predicates:   # 断言,也就是路由条件 ,这里使用了path作为路由条件
            - Path=/order/**
        - id: user-microservice-router
          uri: http://127.0.0.1:8081
          predicates:
            - Path=/user/**
#eureka 配置
eureka:
  client:
    service-url:
      # eureka server url
      defaultZone: http://EurekaServerA:9090/eureka,http://EurekaServerB:9091/eureka
    register-with-eureka: true
    fetch-registry: true
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

其中我们配置了2个路由,path=/order 开头的转到http://127.0.0.1:7070 服务上,也就是咱们的订单服务,path=/user 开头的转到http://127.0.0.1:8081 服务上,也就是我们的用户服务。

2.2 启动并测试

我们首先将 Eureka Server集群 9090与9091提起来,然后再将用户服务与订单服务提起来,在起订单服务之前为了测试方便 需要给订单服务里面添加个接口:
我们在订单服务spring-cloud-order-service-provider 的controller里面添加个接口(之前的getTodayFinishOrderNum方法不用动,用户服务还要调用它)

@RestController
@RequestMapping("/order/data")
public class OrderStatisticServiceController {
    @Value("${server.port}")
    private Integer port;

    @Value("${spring.application.name}")
    private String appName;
    /**
     * 根据用户id获取今日完单数
     * @param id 用户ID
     * @return  完单数
     */
    @GetMapping("/getTodayFinishOrderNum/{id}")
    public Integer getTodayFinishOrderNum(@PathVariable("id") Integer id){
        System.out.println("我是"+port);
        if (port==7070){
            try {// 睡眠10s
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return port;
    }
    // 获取appName
    @GetMapping("/getAppName/{id}")
    public String  getAppName(@PathVariable("id") Integer id){
        return appName;
    }
}

然后将服务提起来,可以看到已经全部起来了,将gateway项目也提起来。
在这里插入图片描述
浏览器请求:http://127.0.0.1:8020/order/data/getAppName/100 ,注意咱们这个ip与port是网关服务的
在这里插入图片描述
这时候可以看到网关将咱们的请求打到order服务上了。

浏览器再请求:http://127.0.0.1:8020/user/data/getTodayStatistic/100
在这里插入图片描述
网关就把咱们这次请求打到user服务上了。

3.路由规则

在springcloud gateway 中内置了很多 Predicates 功能,实现了很多路由匹配规则匹配到对应的路由。

断言类型 描述
DateTime 时间断言 根据请求时间在配置的时间的前面,后面,之前
Cookie 类断言 指定Cookie正则匹配指定值
Header 请求头 指定Header正则匹配指定值,请求头中是否包含某个属性
Host请求主机断言 请求Host匹配指定值
Method请求方式断言 请求Method匹配指定请求方式
Path 请求路径类断言 请求路径正则匹配指定值
QueryParam 请求参数 查询参数正则匹配指定值
RemoteAddr远程地址类断言 请求远程地址匹配指定值

4.动态路由

在上面第二节的简单使用中,我们把uri 这个目标服务器写死了,固定到了某个ip:port上面,其实springcloud gateway支持从注册中心获取服务列表进行访问,能够实现动态路由。下面我们就演示下动态路由

4.1 网关服务修改

4.1.1 配置文件修改

这里我们需要把uri 配置成 lb://在注册中心注册的服务名

server:
  port: 8020
spring:
  application:
    name: spring-cloud-gateway-server
  cloud:
    gateway:

      # 配置路由,可以配置多个
      routes:
        - id: order-microservice-router  # id 自定义路由的id
          # uri就是目标服务地址,这里使用服务名的方式,gateway会帮我们去注册中心中获取服务列表
          uri: lb://spring-cloud-order-service-provider
          predicates:   # 断言,也就是路由条件 ,这里使用了path作为路由条件
            - Path=/order/**
        - id: user-microservice-router
          uri: lb://spring-cloud-user-service-consumer
          predicates:
            - Path=/user/**
#eureka 配置
eureka:
  client:
    service-url:
      # eureka server url
      defaultZone: http://EurekaServerA:9090/eureka,http://EurekaServerB:9091/eureka
    register-with-eureka: true
    fetch-registry: true
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

4.1.2 项目启动类修改

在项目启动类上面添加@EnableDiscoveryClient的注解,表示开启Eureka
在这里插入图片描述

4.2 重启并测试

重启网关服务,在测试前,我们为了测试方便还是需要修改订单服务的controller方法,将端口加到返回值中。

@RestController
@RequestMapping("/order/data")
public class OrderStatisticServiceController {
    @Value("${server.port}")
    private Integer port;
    @Value("${spring.application.name}")
    private String appName;
    /**
     * 根据用户id获取今日完单数
     * @param id 用户ID
     * @return  完单数
     */
    @GetMapping("/getTodayFinishOrderNum/{id}")
    public Integer getTodayFinishOrderNum(@PathVariable("id") Integer id){
        System.out.println("我是"+port);
        if (port==7070){
            try {// 睡眠10s
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return port;
    }
    // 获取appName
    @GetMapping("/getAppName/{id}")
    public String  getAppName(@PathVariable("id") Integer id){
        // appName +port
        return appName+String.valueOf(port);
    }
}

我们重启下订单服务,然后进行测试。
浏览器访问:http://127.0.0.1:8020/order/data/getAppName/100,多访问几次
在这里插入图片描述
在这里插入图片描述
我们可以看到有7070的也有7071的,说明我们配置的动态路由生效了。

5.过滤器

5.1 过滤器介绍

在springchoud gateway中 过滤器可以分为Global过滤器与Gateway过滤器

过滤器类型 说明
Global过滤器 这种就是作用在所有的路由上
Gateway过滤器 这种就是作用在指定的路由上面

然后根据过滤器的生命周期分可以分为 pre过滤与post过滤

生命周期点 介绍
pre 这个就是请求来的时候,可以对请求做一些动作,我们可利用这种过滤器实现身份验证,在集群中选择请求的微服务,记录调试信息等。
post 这个就是响应过来的时候,可以对响应做某些过滤动作,这种过滤器可用来为响应添加标准的 HTTP Header,收集统计信息和指标,将响应从微服务发送给客户端等。

5.2自定义过滤器

我们这里自定义一个全局过滤器,然后实现一个黑白名单的功能。
这里我们写一个BlackListFilter类然后实现GlobalFilter, Ordered 接口

@Component
public class BlackListFilter  implements GlobalFilter, Ordered {

    // 这里模拟下黑名单
    private static final List<String>  blackList=new ArrayList<>();
    static {

        blackList.add("127.0.0.1");// 模拟本机地址
    }
    /**
     * 进行过滤操作
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // 获取请求
        ServerHttpRequest request = exchange.getRequest();

        // 获取响应
        ServerHttpResponse response = exchange.getResponse();
        // 获取客户端ip
        String host = request.getRemoteAddress().getHostString();
        System.out.println("remote host:"+host);
        if (blackList.contains(host)){  // 这个客户端ip在黑名单里面
            // 设置响应码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            String data = "拒绝访问";
            DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
            HttpHeaders headers = response.getHeaders();
            // 设置中文乱码
            headers.add("content-type", "text/html;charset=utf-8");
            return response.writeWith(Mono.just(wrap));
        }
        // 放行到下一个过滤器
        return chain.filter(exchange);
    }

    /**
     * 主要是多个过滤器的时候,需要对过滤器排序,
     * 先经过哪个,后经过哪个,数值越小,这个优先级越高
     * @return order优先级
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

实现这个GlobalFilter 接口主要是说明我这个是个全局过滤器并在定义一些过滤的动作,
然后实现这个Ordered 接口主要是对咱们自定义过滤器的优先级做一个设置,数值越小,filter就越早执行,优先级就越高
重启网关项目,然后测试一下:
浏览器输入http://127.0.0.1:8020/order/data/getAppName/100 ,可以看到请求被拦截了。
在这里插入图片描述

6.高可用

我们要想对网关做高可用,就得依赖网关上层的nginx来实现,接下来我们简单配置下nginx 来实现gateway的高可用。

6.1配置网关服务多实例

修改网关服务的appliction.yml

spring:
  application:
    name: spring-cloud-gateway-server
  cloud:
    gateway:

      # 配置路由,可以配置多个
      routes:
        - id: order-microservice-router  # id 自定义路由的id
          # uri就是目标服务地址,这里使用服务名的方式,gateway会帮我们去注册中心中获取服务列表
          uri: lb://spring-cloud-order-service-provider
          predicates:   # 断言,也就是路由条件 ,这里使用了path作为路由条件
            - Path=/order/**
        - id: user-microservice-router
          uri: lb://spring-cloud-user-service-consumer
          predicates:
            - Path=/user/**
#eureka 配置
eureka:
  client:
    service-url:
      # eureka server url
      defaultZone: http://EurekaServerA:9090/eureka,http://EurekaServerB:9091/eureka
    register-with-eureka: true
    fetch-registry: true
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

---
spring:
     profiles: g1
server:
  port: 8020
---
spring:
     profiles: g2
server:
  port: 8021

这样启动的时候可以配置一下idea
在这里插入图片描述
这样我们就有了8020 与8021 两个网关实例

6.2 nginx配置

如果没有nginx ,可以去 http://nginx.org/en/download.html 下载一个,这个地方选择自己系统下载就好了。
然后就是nginx安装,window 解压就能用,linux系统稍微麻烦些也可以使用yum安装,mac系统可以直接使用brew 安装 brew install nginx
接着就是nginx的配置文件了,修改nginx.conf

#user  nobody;
worker_processes  1;
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    #access_log  logs/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    #keepalive_timeout  0;
    keepalive_timeout  65;
    #gzip  on;
    upstream gateway {
  		server 127.0.0.1:8020;
  		server 127.0.0.1:8021;
	}
    server {
        listen       80;
        server_name  localhost;

        location / {
            proxy_pass http://gateway;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

这里主要是

 upstream gateway {
  		server 127.0.0.1:8020;
  		server 127.0.0.1:8021;
 }
location / {
       proxy_pass http://gateway;
}

接着启动一下nginx

6.3 测试访问

因为咱们nginx是80端口,所以直接在浏览器访问:http://127.0.0.1/order/data/getAppName/100
在这里插入图片描述
接着咱们模拟一个网关服务挂了(手动关闭一个8021)
然后访问,还是能访问到
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/yuanshangshenghuo/article/details/107101640