Spring Cloud Gateway是类似Nginx的网关路由代理,有替代原来Spring cloud zuul之意:
Spring 5 推出了自己的Spring Cloud Gateway,支持Java 8、Reactor API,可在Spring Boot 2 使用,看到了响应式组件Reactor,可以理解这个网关方案目标之一是能够采用Reactive 来实现高效率的网关。
想要建立一个Spring Cloud Gateway 的话,在Spring Tool Suite 上可以选择「Gateway」这个Starter,为了能注册到服务发现服务器,也为了能开放gateway/routes 端点,以便观察路由信息,就顺便加入Eureka与Actuator 的Starter,比如在build.gradle 中可以包含:
implementation('org.springframework.boot:spring-boot-starter-actuator') implementation('org.springframework.cloud:spring-cloud-starter-gateway') implementation('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
Spring Cloud Gateway 可以在注册服务器上注册的服务ID,自动建立路由信息,为此,可以如下设定bootstrap.properties:
server.port=5555 spring.cloud.gateway.discovery.locator.enabled=<b>true</b> spring.cloud.gateway.discovery.locator.lowerCaseServiceId=<b>true</b> eureka.instance.preferIpAddress=<b>true</b> eureka.client.serviceUrl.defaultZone=http:<font><i>//localhost:8761/eureka/</i></font><font> management.endpoints.web.exposure.include: gateway </font>
spring.cloud.gateway.discovery.locator.enabled启用了自动根据服务ID建立路由,路由的路径对应会使用大写ID,若想要使用小写ID,可将spring.cloud.gateway.discovery.locator.lowerCaseServiceId设为true;在设置中也开放了gateway端点。必要时,可以使用RouteLocator实现自定义路由的方式。
接下来启动相关服务,启动Spring Cloud Gateway,默认会跑在Netty上,如果测试请求http://localhost:5555/actuator/gateway/routes的话,就可以看到以下:
[ { <font>"route_id"</font><font>: </font><font>"CompositeDiscoveryClient_ACCTSVI"</font><font>, </font><font>"route_definition"</font><font>: { </font><font>"id"</font><font>: </font><font>"CompositeDiscoveryClient_ACCTSVI"</font><font>, </font><font>"predicates"</font><font>: [ { </font><font>"name"</font><font>: </font><font>"Path"</font><font>, </font><font>"args"</font><font>: { </font><font>"pattern"</font><font>: </font><font>"/acctsvi/**"</font><font> } } ], </font><font>"filters"</font><font>: [ { </font><font>"name"</font><font>: </font><font>"RewritePath"</font><font>, </font><font>"args"</font><font>: { </font><font>"regexp"</font><font>: </font><font>"/acctsvi/(?<remaining>.*)"</font><font>, </font><font>"replacement"</font><font>: </font><font>"/${remaining}"</font><font> } } ], </font><font>"uri"</font><font>: </font><font>"lb://ACCTSVI"</font><font>, </font><font>"order"</font><font>: 0 }, </font><font>"order"</font><font>: 0 }, ... ] </font>
每个路由设定会有个route_id作为识别,在路由定义的predicates中,可以看到设置了Path,这是Spring Cloud Gateway内建的断言器工厂Bean名称,pattern这个设置表示对于http://localhost:5555/acctsvi/xxxx的请求会转给uri设定的值:lb://ACCTSVI,也就是说路由转给了服务ID为ACCTSVI的服务。
filters中设置了RewritePath,这是个过滤器工厂Bean名称,依照regexp的规则,会捕捉请求中的/acctsvi/之后的部份,套用至服务的URI上,也就是http://localhost:5555/acctsvi/xxxx的请求,将会路由转发至http://acctsvi-uri/xxxx。
predicates与filters是Spring Cloud Gateway的重要特性,predicates断言哪些路径符合路由定义,filters设置具体哪些路径适用什么样的具体过滤器,除了设置之外,必要时,都可以代码自己定义。
Spring Cloud Gateway也内建了一些 断言器工厂 与 过滤器工厂 ,这些工厂类别,是可以通过属性档来定义的,必要时,也可以 自定义工厂类别 。
就以上的设置来说,请求http://localhost:5555/acctsvi/accountByName?username=caterpillar就可以得到以下回应:
{ <font>"name"</font><font>: </font><font>"caterpillar"</font><font>, </font><font>"email"</font><font>: </font><font>"caterpillar@openhome.cc"</font><font>, </font><font>"password"</font><font>: </font><font>"$2a$10$CEkPOmd.Uid2FpIOHA6Cme1G.mvhWfelv2hPu7cxZ/vq2drnXaVo."</font><font>, </font><font>"_links"</font><font>: { </font><font>"self"</font><font>: { </font><font>"href"</font><font>: </font><font>"http://Justin-2017:8084/accountByNameEmail?username=caterpillar"</font><font> } } } </font>
如果想要自定义路由,可以写个application.yml(若不想自动建立路由,可以将spring.cloud.gateway.discovery.locator.enabled与spring.cloud.gateway.discovery.locator.lowerCaseServiceId注解掉):
spring: application: name: gateway cloud: gateway: routes: - predicates: - Path=/acct<font><i>/** filters: - StripPrefix=1 uri: lb://acctsvi - predicates: - Path=/msg/** filters: - StripPrefix=1 uri: lb://msgsvi - predicates: - Path=/email/** filters: - StripPrefix=1 uri: lb://email </i></font>
上述配置filters中的StripPrefix也是内建的过滤器工厂Bean名称,设定值为1表示将路由中的第一个层去除,其余保留用来转发请求,请求http://localhost:5555/actuator/gateway/routes的话,就可以看到以下:
[ { "route_id": "545d278b-192b-4370-8156-161815957f91", "route_definition": { "id": "545d278b-192b-4370-8156-161815957f91", "predicates": [ { "name": "Path", "args": { "_genkey_0": "/acct/**" } } ], "filters": [ { "name": "StripPrefix", "args": { "_genkey_0": "1" } } ], "uri": "lb://acctsvi", "order": 0 }, "order": 0 }, ... ]
也就是对http://localhost:5555/acct/accountByName?username=caterpillar的请求,会转给http://acctsvi-url/accountByName?username=caterpillar。
如果想要设定api前置路径,就是修改一下StripPrefix=1为StripPrefix=2:
spring: application: name: gateway cloud: gateway: default-filters: - StripPrefix=2 routes: - predicates: - Path=/api/acct/** uri: lb://acctsvi - predicates: - Path=/api/msg/** uri: lb://msgsvi - predicates: - Path=/api/email/** uri: lb://email
对于每个路由都要套用的过滤器,可以使用default-filters来设置,就以上设定来说,可以请求http://localhost:5555/api/acct/accountByName?username=caterpillar来取得使用者信息。
一开始自动根据服务ID建立路由时,可以看到RewritePath,它也是内建的过滤器工厂,可以运用 规则表示式 来进行路径重写,因此,也可以这么设置api前置:
spring: application: name: gateway cloud: gateway: default-filters: - RewritePath=/api/.*?/(?<remaining>.*), /$/{remaining} routes: - predicates: - Path=/api/acct/** uri: lb://acctsvi - predicates: - Path=/api/msg/** uri: lb://msgsvi - predicates: - Path=/api/email/** uri: lb://email
就目前的设定来说,在客户端的部份,〈 使用Zuul 〉中的gossip就可以了,毕竟交互的接口没有改变,但是因为使用spring.application.gateway作为应用代理了,还是要记得改一下@FeignClient中的服务ID为gateway。
可以在 Gateway 中找到以上的源码