Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

从0开始构建你的api网关--Spring Cloud Gateway网关实战及原理解析

一天不进步,就是退步 2019-02-21 11:40:00 阅读数:353 评论数:0 点赞数:0 收藏数:0

API 网关

API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:

  1. 客户端会多次请求不同的微服务,增加了客户端的复杂性。
  2. 存在跨域请求,在一定场景下处理相对复杂。
  3. 认证复杂,每个服务都需要独立认证。
  4. 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施。
  5. 某些微服务可能使用了防火墙 / 浏览器不友好的协议,直接访问会有一定的困难。

以上这些问题可以借助 API 网关解决。API 网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过 API 网关这一层。也就是说,API 的实现方面更多的考虑业务逻辑,而安全、性能、监控可以交由 API 网关来做,这样既提高业务灵活性又不缺安全性,典型的架构图如图所示:

![](https://s3.amazonaws.com/infoq.content.live.0/articles/comparing-api-gateway-performances/zh/resources/5081-1523696432553.png)

使用 API 网关后的优点如下:

  • 易于监控。可以在网关收集监控数据并将其推送到外部系统进行分析。
  • 易于认证。可以在网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
  • 减少了客户端与各个微服务之间的交互次数。

API 网关选型

业界的情况:

我前面的文章<Netflix网关zuul(1.x和2.x)全解析>已经介绍了zuul1 和zuul2,现在就尝试从实例入手介绍一下spring cloud gateway

首先我们一步步实现一个最简单的网关例子

步骤1:在http://start.spring.io网站上创建一个spring-cloud-gateway-example项目,依赖spring-cloud-gateway,如下图所示

此时生产了一个spring-cloud-gateway-example的空项目包,pom.xml文件如下 4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE com.example spring-cloud-gateway-example 0.0.1-SNAPSHOT spring-cloud-gateway-example Demo project for Spring Boot 1.8 Greenwich.RELEASE org.springframework.cloud spring-cloud-starter-gateway org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin spring-milestones Spring Milestones https://repo.spring.io/milestone

2.创建一个Route实例的配置类GatewayRoutes

packagecom.example.springcloudgatewayexample;importorg.springframework.cloud.gateway.route.RouteLocator;importorg.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration; @Configurationpublic classGatewayRoutes { @BeanpublicRouteLocator routeLocator(RouteLocatorBuilder builder) {returnbuilder.routes() .route(r->r.path("/java///") .filters( f-> f.stripPrefix(1) ) .uri("http://localhost:8090/helloWorld") ) .build(); } }

当然,也可以不适用配置类,使用配置文件,如下图所示

spring: cloud: gateway: routes: - predicates: - Path=/java/// filters: - StripPrefix=1 uri: "http://localhost:8090/helloWorld"

不过,为了调试方便,我们使用配置类方式。

此时项目已经完成,足够简单吧。

3.启动此项目

  >>因api网关需要转发到一个服务上,本文为http://localhost:8090/helloWorld,那需要先启动我上文<spring boot整合spring5-webflux从0开始的实战及源码解析>,你也可以创建一个普通的web项目,启动端口设置为8090,然后启动。. /\ / '_ () ( ( )_ | ' | '| | ' / ` | \/ )| |)| | | | | || (| | ) ) ) ) ' || .|| ||| |_, | / / / / =========||==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.3.RELEASE) 2019-02-21 09:29:07.450 INFO 11704 --- [ main] c.e.demo.Spring5WebfluxApplication : Starting Spring5WebfluxApplication on DESKTOP-405G2C8 with PID 11704 (E:workspaceForCloudspring5-webfluxtargetclasses started by dell in E:workspaceForCloudspring5-webflux) 2019-02-21 09:29:07.455 INFO 11704 --- [ main] c.e.demo.Spring5WebfluxApplication : No active profile set, falling back to default profiles: default 2019-02-21 09:29:09.409 INFO 11704 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8090 2019-02-21 09:29:09.413 INFO 11704 --- [ main] c.e.demo.Spring5WebfluxApplication : Started Spring5WebfluxApplication in 2.304 seconds (JVM running for 7.311)

 >>以spring boot方式启动spring-cloud-gateway-example项目,日志如下

2019-02-21 10:34:33.435 INFO 8580 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$1e059320] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) . ____ _ __ _ _ /\ / ___'_ __ _ _(_)_ __ __ _ ( ( )___ | '_ | '_| | '_ / _` | \/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |___, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.3.RELEASE) 2019-02-21 10:34:33.767 INFO 8580 --- [ main] e.s.SpringCloudGatewayExampleApplication : No active profile set, falling back to default profiles: default 2019-02-21 10:34:34.219 INFO 8580 --- [ main] o.s.cloud.context.scope.GenericScope : BeanFactory id=d98183ec-3e46-38ba-ba4c-e976a1017dce 2019-02-21 10:34:34.243 INFO 8580 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$1e059320] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 2019-02-21 10:34:44.367 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [After] 2019-02-21 10:34:44.367 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Before] 2019-02-21 10:34:44.367 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Between] 2019-02-21 10:34:44.367 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Cookie] 2019-02-21 10:34:44.367 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Header] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Host] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Method] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Path] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Query] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [ReadBodyPredicateFactory] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [RemoteAddr] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Weight] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [CloudFoundryRouteService] 2019-02-21 10:34:44.920 INFO 8580 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080 2019-02-21 10:34:44.923 INFO 8580 --- [ main] e.s.SpringCloudGatewayExampleApplication : Started SpringCloudGatewayExampleApplication in 12.329 seconds (JVM running for 13.126)

4.测试,浏览器访问http://localhost:8080/java/helloWorld

返回hello world !

5.从上面的代码和配置及实例中,我们可以看出spring cloud gateway处理request请求的流程如下所示:

即在最前端,启动一个netty server(默认端口为8080)接受请求,然后通过Routes(每个Route由Predicate(等同于HandlerMapping)和Filter(等同于HandlerAdapter))处理后通过Netty Client发给响应的微服务。

那么在gateway本身最重要的应该是Route(Netty Server和Client已经封装好了),它由RouteLocatorBuilder构建,内部包含Predicate和Filter,private Route(String id, URI uri, int order, AsyncPredicate predicate, ListgatewayFilters) {this.id =id;this.uri =uri;this.order =order;this.predicate =predicate;this.gatewayFilters =gatewayFilters; }

那么我们就来探讨一下这两个组件吧

5.1.Predicate

Predicte由PredicateSpec来构建,主要实现有:

以path为例//// A predicate that checks if the path of the request matches the given pattern /@parampatterns the pattern to check the path against. / The pattern is a {@linkorg.springframework.util.PathMatcher} pattern /@returna {@linkBooleanSpec} to be used to add logical operators/*/ publicBooleanSpec path(String... patterns) {return asyncPredicate(getBean(PathRoutePredicateFactory.class) .applyAsync(c->c.setPatterns(Arrays.asList(patterns)))); }

PathRoutePredicateFactory中执行

@Overridepublic Predicateapply(Config config) {final ArrayList pathPatterns = new ArrayList<>();synchronized (this.pathPatternParser) { pathPatternParser.setMatchOptionalTrailingSeparator( config.isMatchOptionalTrailingSeparator()); config.getPatterns().forEach(pattern->{ PathPattern pathPattern= this.pathPatternParser.parse(pattern); pathPatterns.add(pathPattern); }); }return exchange ->{ PathContainer path=parsePath(exchange.getRequest().getURI().getPath()); Optional optionalPathPattern =pathPatterns.stream() .filter(pattern->pattern.matches(path)).findFirst();if(optionalPathPattern.isPresent()) { PathPattern pathPattern=optionalPathPattern.get(); traceMatch("Pattern", pathPattern.getPatternString(), path, true); PathMatchInfo pathMatchInfo=pathPattern.matchAndExtract(path); putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());return true; }else{ traceMatch("Pattern", config.getPatterns(), path, false);return false; } }; }

5.2.Filter

Filter分两种,一种GatewayFilter,一种GlobalFilter

5.2.1 GatewayFilter

GatewayFilter由GatewayFilterSpec构建,GatewayFilter的构建器

 

5.2.2 GlobalFilter

5.3 GlobalFilter和GatewayFilter的联系

FilteringWebHandler.GatewayFilterAdapter代理了GlobalFilter

6.总结

  本文从一个spring-cloud-gateway实例入手,深入浅出的介绍了spring-cloud-gateway的组件,并从源码角度给出了实现的原理。

   spring-cloud-gateway在最前端,启动一个netty server(默认端口为8080)接受请求,然后通过Routes(每个Route由Predicate(等同于HandlerMapping)和Filter(等同于HandlerAdapter))处理后通过Netty Client发给响应的微服务。

 Predicate和Filter的各个实现定义了spring-cloud-gateway拥有的功能。

参考资料:

【1】https://www.infoq.cn/article/comparing-api-gateway-performances

【2】https://dzone.com/articles/spring-cloud-gateway-configuring-a-simple-route

版权声明
本文为[一天不进步,就是退步]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/davidwang456/p/10411451.html