我在使用Spring Gateway时遇到的一些坑

7,149 阅读6分钟

前言

最近在公司用Spring Cloud 全家桶搭建一套完整的微服务,在网关选型的时候,因为我们公司可能会用到websocket,所以放弃zuul而改用Spring Cloud Gateway,本文记录下在使用gateway过程中遇到的一些坑和注意点。

正文

Gateway的介绍和基本的搭建就不说了,可以按照官方文档走。Spring Cloud Gateway和zuul的区别也不提了,网上也有很多文章。使用gateway直接引入依赖就行,不用在启动类加注解。

1. 请求转发

我在这里把请求转发大概的分成两类:

a.未整合注册中心

其实这种用法比较少,一般来说我们都会整合注册中心使用,这样可以达到自动配置自动转发。当然这种特定的方式也许某些场合也会用到。

这种情况下就需要你指定你需要转发到哪个服务器上去,这边可以分为两种,配置文件和代码形式。

a.1 配置文件

来个样例

spring:
  application:
    name: spring-cloud-gateway-sample
  cloud:
    gateway:
      routes:
        - id: blog
          uri: https://juejin.cn
          predicates:
            # 匹配路径转发
            - Path=/api-boot-datasource-switch.html
# 端口号
server:
  port: 9090

几个名词, id是目前所写的路由的id,id前面有个 -, 这是yml的写法,相当于一个list的多个元素,你可以写多个路由,用-id 来区别开来。

uri就是你需要转发的地址了。因为没有整合注册中心所以需要写全。

这里解释下 Predicates吧,每一个Predicate的使用,你可以理解为:当满足这种条件后才会被转发,如果是多个,那就是都满足的情况下被转发。比如上面的例子就是当访问http://localhost:9090/api-boot-datasource-switch.html时就会被自动转发到http://juejin.im/api-boot-datasource-switch.html,这里要注意完全匹配Path的值时才会进行路由转发。

所以上面的例子是谓词Path的样例,还有很多谓词,比如 Before, After,Cookie, Header, Host等等,他们也可以组合出现。 全面的你可以参考官方文档或者这篇文章,写的也比较全面:www.jianshu.com/p/d2c3b6851…

a.2 RouteLocator

代码的形式可以通过 RouteLocator 类来实现:

@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
  return builder.routes()
    .route("blog", r -> 
           r.path("/api-boot-datasource-switch.html").uri("https://juejin.cn"))
    .build();
}

这个就是上面使用Path谓词的代码体现,讲这个类以@Bean的形式注入到spring中就行。其他的谓词类似,不提了。

b.整合注册中心

这个其实是日常工作中比较常用的,这里我是用的eureka,其他的注册中心应该类似。

这里我用了配置文件的方式实现了全局的配置,

spring:
  application:
    name: spring-cloud-gateway-sample
  cloud:
    gateway:
      routes:
        - id: blog
          uri: lb://blog-service
          predicates:
            # 匹配路径转发
            - Path=/blog/**
# 端口号
server:
  port: 9090

上面的例子和配置未整合时的yml类似,唯一不同的就是 uri的配置和Path的条件,这里使用了 lb:// 的形式,代表从注册中心获取服务,后面接的就是你需要转发到的服务名称,这个服务名称必须跟eureka中的对应,否则会找不到服务。所以上面的配置文件就是当你的请求路径端口号后以/blog开头的都转发到 blog-serivce上去,参数不变。

第二种就是我比较推荐的,默认全局的配置,只要你的路径中以微服务的服务名称开头,都会自动转发到该服务上去。 配置如下

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lowerCaseServiceId: true

配置完成之后我们就可以通过小写的服务名称进行访问了,比如 https://gatewayIp:gatewayPort/blog/article/all 就会自动转发到 http://blogserviceIp:blogservicePort/article/all

2. 跨域

这个问题,你在其他平台几乎可以看到千篇一律的解决方案并且亲自实践后发现并没有起到作用(可能是gateway自升级后这些解法已经不支持,至少我亲自实践后确实不起作用)。不过我还是遇到了一种比较好的写法,能够合理的解决跨域的问题,亲测有效。如果有更好的解法也欢迎分享。

贴上原文链接: blog.csdn.net/xht555/arti…

另外还有一个,实践后没有起到作用,也贴出来大家参考下:www.jianshu.com/p/a46e62f9a…

3. docker容器

我司用docker容器来作为微服务的linux容器,当容器和spring cloud eureka结合的时候,会遇到一个坑,这是我在使用zuul网关请求的时候就遇到的。

Zuul在转发请求的时候,会根据eureka注册表里的各个微服务的ip+port去寻找地址,但是坑爹的是,当你的微服务在docker容器中时,注册进去的形式是 docker容器ID + port, 类似于你在eureka的监控面板看到的是 R3FDS4G:8080,这样你的网关在请求转发的时候,会报 server unknow 的错误,而且这个问题在本地是不存在的因为你本地一直都是localhost。

解决办法是,在你的eureka client的配置文件中,将你的server 以ip+port的形式注册进去。

eureka:
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}:${spring.application.name}

好了,当你成功的将你的容器以ip+port的形式注册到eureka中时,这时候还是有问题,虽然网关能够成功的将请求转发,但是这个时候是ping不通的,你点开eureka的监控面板,可以发现,此时微服务的ip地址并不是宿主机的地址。这个其实就要说到docker通信问题了,docker默认的是bridge模式,容器会自己在内部开辟空间,外部是连接不通的。所以docker启动的时候,需要在命令行里加上 -net=host,这样容器就会挂载到宿主机上了。关于docker通信问题可以自行查阅资料。这个时候网关就能成功的转发请求了(指的是整个注册中心)。 如果你使用网关转发的时候已经指定了微服务的服务器路径,那就不会出现上述问题的,只是你每多加一个微服务可能就要再次配置一遍。

总结

以上就是我在工作中遇到的一些问题,以后还有问题会更新。

Spring cloud gateway感觉国内用的还不多,大厂基本自研,小公司又很少用到spring cloud网关组件或者直接nginx做网关,如果有问题可以去GitHub直接和作者聊聊。