不一样的微服务通常有不一样的网络地址,而外部的客户端可能须要调用多个服务的接口才能完成一个业务需求。好比一个电影购票的收集APP,可能回调用电影分类微服务,用户微服务,支付微服务等。若是客户端直接和微服务进行通讯,会存在一下问题:java
上述问题,均可以借助微服务网关解决。微服务网关是介于客户端和服务器端之间的中间层,全部的外部请求都会先通过微服务网关,架构演变成:web
这样客户端只须要和网关交互,而无需直接调用特定微服务的接口,并且方便监控,易于认证,减小客户端和各个微服务之间的交互次数正则表达式
Zuul是Netflix开源的微服务网关,他能够和Eureka,Ribbon,Hystrix等组件配合使用。Zuul组件的核心是一系列的过滤器,这些过滤器能够完成如下功能:spring
Spring Cloud对Zuul进行了整合和加强。目前,Zuul使用的默认是Apache的HTTP Client,也可使用Rest Client,能够设置ribbon.restclient.enabled=true.后端
<properties> <java.version>1.8</java.version> <spring-cloud.version>Finchley.M9</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> </dependencies> <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>
对于 spring-cloud-starter-netflix-zuul 依赖,能够经过查看它的依赖内容了解到,该模块中不只包含了 Netflix Zuul 的核心依赖 zuul-core,还包含了 hystrix、ribbon、actuator 等依赖。api
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @EnableZuulProxy //开启zuul网关服务功能 @@SpringBootApplication public class ApiGatewayApplication { public static void main(String[] args) { SpringApplication.run(ApiGatewayApplication.class, args); } }
配置 Zuul 应用的基础信息,如应用名、服务端口号等,具体内容以下:跨域
spring.application.name=api-gateway server.port=5555
完成上面的工做后,经过 Zuul 实现的 API 网关服务就构建完毕了。安全
下面,咱们将经过一个简单的示例来为上面构建的网关服务增长请求路由的功能。为了演示请求路由的功能,咱们现将以前准备的 Eureka 服务注册中心和微服务应用都启动起来。此时,咱们在 Eureka信息面板中能够看到以下图所示的微服务应用已经被注册成功了。服务器
传统路由方式没有与注册中心进行整合,须要维护各个路由path 与 url 的关系。网络
经过 zuul.routes.<route>.path 与 zuul.routes.<route>.url 参数对的方式进行配置。
zuul.routes.hello-service.path=/hello-service/** zuul.routes.hello-service.url=http://localhost:8080/
该配置实现了对符合 /hello-service/** 规则的请求路径转发到 http://localhost:8080/ 地址的路由规则。好比一个请求 http://localhost:5555/hello-service/hello 被发送到 API 网关上,因为 /hello-service/hello 可以被上面配置的 path 规则匹配,因此 API 网关会被转发请求到 http://localhost:8080/hello 的地址。
经过 zuul.routes.hello-service.path 与 zuul.routes.hello-service.service-id 参数对的方式进行配置。
zuul.routes.hello-service.path=/hello-service/** zuul.routes.hello-service.service-id=hello-service ribbon.eureka.enabled=false hello-service.ribbon.listOfServers=http://localhost:8080/,http://localhost:8081/
注意:
Spring Cloud Zuul 经过与 Spring Cloud Eureka 的整合,实现了对服务实例的自动化维护,因此在使用服务路由配置的时候,不须要像传统路由配置方式那样为 serviceId 指定具体的服务实例地址,只须要经过 zuul.routes.<route>.path 与 zuul.<route>.serviceId 参数对的方式进行配置便可。
zuul.routes.hello-service.path=/hello-service/** zuul.routes.hello-service.service-id=hello-service
也可使用一种更简洁的配置方式
zuul.routes.hello-service=/hello-service/**
在使用服务路由配置以前,先添加 eureka client 依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
application.properties 中添加注册中心配置。
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/
启动类上的 @SpringBootApplication 注解换成 @SpringCloudApplication 注解(@SpringCloudApplication 注解引入了 @SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker 注解)。
@EnableZuulProxy //开启zuul网关服务功能 @SpringCloudApplication public class ApiGatewayApplication { public static void main(String[] args) { SpringApplication.run(ApiGatewayApplication.class, args); } }
当咱们为 Spring Cloud Zuul 构建的 API 网关服务引入 Spring Cloud Eureka 以后,它为 Eureka 中的每一个服务都自动建立一个默认路由规则,这些默认规则的 path 会使用 serviceId 配置的服务名做为请求前缀,就如上面的例子那样。
zuul.routes.hello-service.path=/hello-service/** zuul.routes.hello-service.service-id=hello-service
因为默认状况下全部Eureka 上的服务都会被 Zuul 自动地建立映射关系来进行路由,这会使得一些咱们不但愿对外开放的服务也可能被外部访问到。这个时候,咱们可使用 zuul.ignored-services 参数来设置一个服务名匹配表达式来定义不自动建立路由的规则。
# 对全部的服务都不自动建立路由 zuul.ignored-services=*
可使用 PatternServiceRouteMapper 对象来自定义服务于路由映射的生成关系。
例如,下面的例子为 hello-service-v1 的微服务的服务名建立相似 v1/hello-service/** 的路由规则。
@Bean public PatternServiceRouteMapper serviceRouteMapper(){ // servicePattern: 指的是微服务的pattern // routePattern: 指的是路由的pattern return newPatternServiceRouteMapper( "(?<name>^.+)-(?<version>v.+$)", "${version}/${name}" ); }
注:
Zuul 提供了一个忽略表达式参数 zuul.ignored-patterns,用于设置不但愿被 API 网关进行路由的 URL 表达式。
如西面的设置会使得路径中包含 /hello/ 的 url 不被路由。
zuul.ignored-patterns=/**/hello/**
zuul 提供了 zuul.prefix 参数来为网上上的路由的规则增长前缀信息。
如进行了下面的设置以后,访问 hello-service 的 hello 接口须要使用 http://localhost:5555/api/hello-service/hello。
spring.application.name=api-gateway server.port=5555 zuul.prefix=/api zuul.routes.hello-service=/hello-service/**
zuul 中默认会移除匹配到的 url 中的 serviceId 部分,如 对于下面的配置,当网关遇到 /hello-service/hello 的请求时,会去找请求 hello-service 服务的 hello 接口(没有服务名最为前缀)。
zuul.routes.hello-service=/hello-service/**
若是服务配置了 context-path,则由于 zuul 会移除 url 中的 serviceId,就会致使 404。这个时候,就须要使用 zuul.routes.<route>.strip-prefix 来设置路由时不去掉 serviceId。
# 默认状况下,在转发请求以前,从请求中删除serviceId(使用zuul.stripPrefix = false关闭此行为) #zuul.stripPrefix=false # 路由时不去掉 serviceId(若是调用的服务配置了 context-path,则不能去掉) zuul.routes.hello-service.strip-prefix=false
zuul.stripPrefix 彷佛有点问题,没法实现 zuul.routes.<route>.strip-prefix 的效果。
在 zuul 实现的 API 网关路由功能中,还支持 forward 形式的服务端跳转配置。实现方式很是简单,只需经过使用 path 与 url 的配置方式就能完成。经过 url 中使用 forward 来指定须要跳转的服务器资源路径。
以下面的配置实现了将符合 /local/** 规则的请求在 API 网关上实现本地跳转。
# 本地跳转 zuul.routes.local.path=/local/** zuul.routes.local.url=forward:/local
好比,当 API 网关接到请求 /local/hello ,会转发到网关的 /local/hello 请求上进行本地处理(须要在 API 网关上增长一个 /local/hello 接口)。
因为 spring-cloud-starter-netflix-zuul 依赖,包含了 hystrix、ribbon 模块的依赖,因此 Zuul 天生就拥有线程隔离和客户端负载均衡功能。可是须要注意,当使用 path 与 url 的映射关系来配置路由规则的时候,对于路由转发的请求不会采用 HystrixCommand 来包装,因此这里路由请求没有线程隔离和断路器的保护,而且也不会有负载均衡的能力。所以,咱们在使用 Zuul 的时候尽可能使用 path 和 serviceId 的组合来进行配置,这样不只能够包装 API 网关的健壮和稳定,也能用到 Ribbon 的客户端负载均衡功能。
另外,关于 zuul 中各类超时时间等配置,能够参考: SpringCloud的各类超时时间配置效果
本文参考:使用Zuul构建API Gateway