微服务网关(gateway)概述 与 嵌入式 Zuul 反向代理

目录

微服务网关 gateway 概述

路由器网关 Zuul 概述

嵌入式 Zuul 反向代理

ignored-services 不代理指定服务

path 与 serviceId 细粒度控制

prefix  & stripPrefix 指定路由前缀

ignoredPatterns 不代理指定路径

zuul.routes 路由优先级

Zuul Http Client(客户端)

Cookies and 敏感头信息

/routes & /filters 端点查看路由信息


微服务网关 gateway 概述

1、想象一下一个购物应用程序的产品详情页面展示了指定商品的信息:

2、若是采用微服务架构,显示在产品页上的数据会分布在不同的微服务上,比如:

购物车服务——购物车中的件数

订单服务——历史订单

目录服务——商品基本信息,如名称、图片和价格

评论服务——客户的评论

库存服务——低库存预警

送货服务——送货选项、期限和费用,这些信息单独从送货方 API 获取

推荐服务——推荐商品

3、客户端访问这些服务通常有如下两种方式:1)客户端与微服务直接通信、2)使用 API 网关构建微服务

4、从理论上讲,客户端可以直接向每个微服务发送请求,如上图客户端需要发送 7 个独立请求,在更复杂的应用程序中,可能要发送更多的请求。随着时间推移,可能需要合并两个服务,或者将一个服务拆分成两个或更多服务,然而如果客户端与微服务直接通信,那么执行这类重构就非常困难了。

5、通常来说使用 API 网关是更好的解决方式。API 网关是一个服务器,也可以说是进入系统的唯一节点。API 网关封装内部系统的架构,并且提供 API 给各个客户端,它可能还具备授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等功能。

6、API 网关负责服务请求路由、组合及协议转换。客户端的所有请求都首先经过 API 网关,然后由它将请求路由到合适的微服务。API 网关经常会通过调用多个微服务并合并结果来处理一个请求。它可以在 web 协议(如 HTTP 与 WebSocket)与内部使用的非 web 友好协议之间转换。

7、Netflix 的 zuul 路由器就是一个很好的 API 网关。Router and Filter: Zuul

更多关于微服务网关的内容可以参考:「Chris Richardson 微服务系列」使用 API 网关构建微服务:http://blog.daocloud.io/microservices-2/

路由器网关 Zuul 概述

1、路由(Routing )是微服务体系结构的一个组成部分,例如 "/" 可能映射到您的 web 应用程序,"/api/users" 映射到用户服务,"/api/shop" 映射到商店服务。

2、Zuul 是 Netflix 的一个基于 JVM 的路由器和服务器端负载均衡器,gitHub 官网地址:https://github.com/Netflix/zuul

3、Netflix Zuul 具有以下特性:Authentication(身份验证)、Insights、Stress Testing(压力测试)、Canary Testing(金丝雀测试)、Dynamic Routing(动态路由)、Service Migration(服务迁移)、Load Shedding(减载)、Security(安全)、Static Response handling(静态响应处理)、Active/Active traffic management

4、配置属性 zuul.max.host.connections 已被两个新属性 zuul.host.maxTotalConnections 和 zuul.host.maxPerRouteConnections 取代,分别默认为 200 和 20。

5、所有路由的默认 Hystrix 隔离模式(ExecutionIsolationStrategy)为 SEMAPHORE,也可以使用 zuul.ribbonIsolationStrategy 更改为 THREAD。

6、在项目中引入 Zuul 依赖即可使用(官网:How to Include Zuul),要启用它,请使用 @EnableZuulProxy 注释在 Spring Boot 启动类上,这样做会导致将本地呼叫转发到适当的服务:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

7、Spring Cloud 创建了一个嵌入式 Zuul 代理,以简化 UI 应用程序要对一个或多个后端服务进行代理调用的常见用例的开发。此功能对于用户界面代理所需的后端服务很有用,从而避免了为所有后端独立管理CORS(跨域)和身份验证问题的需求。

8、Netflix Zuul 启动程序不包括服务发现组件,因此需要另外导入服务发现组件(比如 Eureka-client)。

嵌入式 Zuul 反向代理

1、本文环境 java jdk 1.8 + Spring Boot 2.1.3 + Spring Cloud Greenwich.SR1 版本。官网 :Embedded Zuul Reverse Proxy

1)ec-zebra:斑马微服务,有访问路径:http://localhost:9394/zebra/person/info?id=33980

2)snow-leopard:雪豹微服务,有访问路径:http://localhost:9395/leopard/food/foods?id=998760

3)gateway-server:网关微服务,默认情况下它会代理注册中心上的其它所有微服务,访问规则:http://网关IP:网关port/网关context-path/其它微服务名称/微服务请求路径

http://192.168.3.230:7790/ec-zebra/zebra/person/info?id=33980
http://192.168.3.230:7790/snow-leopard/leopard/food/foods?id=998760

2、ec-zebra、snow-leopard 不做过多分析,主要介绍 gateway-server,网关 pom.xml 文件依赖如下:

<!--网关作为一个单独的微服务客户端-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--zuul依赖内部依赖了 spring-boot-starter-web,actuator,netflix-hystrix,netflix-ribbon,netflix-archaius组件-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

3、网关服务启动类上开启网关代理:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

/**
 * @EnableZuulProxy:开启网关代理,默认未开启。它是一个组合注解,包含了 @EnableCircuitBreaker
 */
@SpringBootApplication
@EnableZuulProxy
public class GatewayZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayZuulApplication.class, args);
    }
}

4、网关服务全局配置文件:

#服务提供者
server:
  port: 7790    #服务器端口

#spring.application.name:spring 应用名,未配置 eureka.instance.appname 时,spring 应用名默认为微服务名称
#其它需要代理的微服务未配置 spring.application.name 时,网关 zuul 会找不到本服务报错如:Load balancer does not have available server for client: xxx
spring:
  application:
    name: gateway-server

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka/ #eureka 服务器地址
  instance:
    prefer-ip-address: true # IP 地址代替主机名注册
    instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} # 微服务节点实例名称

5、到此一个路由网关就已经可以代理了,启动之后用户可以通过网关统一访问所有微服务,当然不通过网关也能访问,测试如下:

ignored-services 不代理指定服务

1、Zuul 默认会代理注册中心上的其它所有微服务,如果不想代理某些服务,则使用 zuul.ignored-services=服务1,服务2... 配置,多个服务服务时用逗号隔开,zuul.ignored-services=* 表示所有的服务都不代理

zuul:
  ignored-services: ec-zebra #对微服务 ec-zebra 不进行网关代理,值是一个 set,多个时用逗号隔开,* 表示所有

2、有的时候并不想直接使用微服务的名称写在 Zull 代理的路径中,而是想改为其它值,如:

http://192.168.3.230:7790/snow-leopard/leopard/food/foods?id=998760

改为

http://192.168.3.230:7790/leopard-server/leopard/food/foods?id=998760

则可以使用 zuul.routes 配置路由规则,此时代理路径中既可以使用服务名 snow-leopard,也可以使用 leopard-server:

zuul:
  #routes 配置路由规则,值是一个 Map,key 是代理的微服务名称,如 snow-leopard
  routes:
    snow-leopard: /leopard-server/** #后缀 "/**" 表示匹配所有请求

综合用例:除了 users 服务,其它所有服务都不代理,代理路径中的服务名称 users 可以使用 myusers 代替 :

 zuul:
  ignoredServices: '*'
  routes:
    users: /myusers/**

所有关于 zuul 的配置都在 org.springframework.cloud.netflix.zuul.filters.ZuulProperties 类中。

path 与 serviceId 细粒度控制

1、要对路由进行更细粒度的控制,可以分别指定 path 和 serviceId(官网),如:

zuul:
  routes:
    route-ec-zebra:
      serviceId: ec-zebra
      path: /zebra-server/**

serviceId 是代理的微服务名称。

path 是映射的路径。

route-ec-zebra  是自定义的值,只要在 routes 下唯一即可,没有强制要求,通常设置成代理的微服务名称即可,此时serviceId 可以省略。

上面等价于下面:

zuul:
  routes:
    ec-zebra: /zebra-server/**

2、zuul 对 /zebra-server 的调用会转发到 ec-zebra 服务,/** 表示分层匹配,比如 /ec-zebra/a/b/c...都有效;/* 表示一级匹配,比如 /ec-zebra/a 有效,/ec-zebra/a/b 匹配失败.

源码:https://github.com/wangmaoxiong/Hystrix-hello-world/blob/master/gateway-zuul/src/main/resources/application.yml

prefix  & stripPrefix 指定路由前缀

1、如果想为所有映射添加前缀/代理前缀,可以使用 zuul.prefix 配置,例如 /api,此时所有的代理路径都需要加上此前缀

2、zuul.stripPrefix 默认为 true,所以在转发请求之前,默认将从请求中剥离(strip)代理前缀。如果为 false,则表示不剥离,则会自动将此前缀转为对微服务的请求路径。

zuul:
  #  ignored-services: ec-zebra #对微服务 ec-zebra 不进行网关代理,值是一个 set,多个时用逗号隔开,* 表示所有
  #routes 配置路由规则,值是一个 Map,key 是代理的微服务名称,如 snow-leopard
  routes:
    snow-leopard: /leopard-server/** #后缀 "/**" 表示匹配所有请求
    #route-ec-zebra 自定义名称,唯一即可,path 是映射路径,serviceId 是代理的服务名称
    route-ec-zebra:
      path: /zebra-server/**
      serviceId: ec-zebra   #注册中心的服务id,即微服务名称。当它有多个节点时,网关默认会使用 ribbon 进行负载均衡

  prefix: /api    #为所有请求设置映射前缀,默认为 ""
  strip-prefix: true  #实际转发时,是否自动去掉此前缀,false 时自动转为请求路径

原微服务访问路径:http://192.168.3.230:9394/zebra/person/info?id=33980
原微服务访问路径:http://192.168.3.230:9395/leopard/food/foods?id=998760

1)未配置 zuul.prefix(默认为 "") 与 zull.strip-prefix(默认为 true) 时,zuul 代理访问路径如下:

http://192.168.3.230:7790/leopard-server/leopard/food/foods?id=998760
http://192.168.3.230:7790/zebra-server/zebra/person/info?id=33980

2)#如 zuul.prefix=/api, 则 zuul 代理请求路径如下:

http://192.168.3.230:7790/api/leopard-server/leopard/food/foods?id=998760  
http://192.168.3.230:7790/api/zebra-server/zebra/person/info?id=33980

3)#如 zuul.strip-prefix=true,则转发时,自动去掉/剥离前缀 /api,实际转发的请求为:
http://192.168.3.230:7790/leopard-server/leopard/food/foods?id=998760
http://192.168.3.230:7790/zebra-server/zebra/person/info?id=33980

4)#如 zuul.strip-prefix=false,则转发时,不剥离前缀 /api,而会自动转为对微服务的请求路径,实际转发的请求为:

http://192.168.3.230:7790/leopard-server/api/leopard/food/foods?id=998760
http://192.168.3.230:7790/zebra-server/api/zebra/person/info?id=33980
#所以此时前缀不能随便配置,必须符合微服务实际的请求路径,通常用于配置 context-path,因为同一微服务所有请求的上下文路径是一样的。

3、zuul.prefix 与 zuul.stripPrefix 配套使用,属于全局配置,对所有微服务有效,如果想对某个微服务进行局部配置,则可以使用 zuul.routes 下的 stripPrefix 配套 path 。如:

zuul:
  #routes 配置路由规则,值是一个 Map,key 是代理的微服务名称,如 snow-leopard,值仍然还可以使用 map
  routes:
    route-snow-leopard:         #自定义名称,整个 routes 下唯一即可
      path: /leopardApp/**         #代理路径
      serviceId: snow-leopard   #代理的微服务名称,当它有多个节点时,网关默认会使用 ribbon 进行负载均衡
      strip-prefix: true        #是否剥离/去掉前缀,默认为 true,表示实际转发路径会去掉 path 中的前缀 leopard
    ec-zebra-toute:
      path: /zebra/**
      serviceId: ec-zebra
      strip-prefix: false     # false 表示不剥离 path 中的前缀 zebra,此时前缀会被当作真实的请求路径

#此时网关请求路径为:
http://192.168.3.230:7790/leopardApp/leopard/food/foods?id=998760  #会自动剥离/去掉前缀 leopardApp 然后转发
http://192.168.3.230:7790/zebra/person/info?id=33980   #不会剥离/去掉前缀 zebra,此时刚好是目标微服务的完整请求路径,通常可用于设置上下文路径

ignoredPatterns 不代理指定路径

1、zuul.ignoredServices 是配置忽略不代理某些微服务,如果需要更细粒度的忽略,则可以用 zuul.ignoredPatterns 指定要忽略的特定模式。 这些模式在路线定位过程开始时进行评估,这意味着模式中应包含前缀以保证匹配。 被忽略的模式跨越所有服务,并取代任何其他路由规范。

2、zuul.ignoredPatterns 用于配置忽略的某些具体路径,忽略后则无法再通过网关请求。它的值是 set ,所以多个路径时,可以用逗号隔开,如:

zuul:
#忽略请求路径中含有 code、cipher 字符串的请求,它们会被拦截,不会再转发
  ignored-patterns: /**/code/**,/**/cipher/**  

所有关于 zuul 的配置都在 org.springframework.cloud.netflix.zuul.filters.ZuulProperties 类中。

zuul.routes 路由优先级

1、如果需要保留路由的顺序,则需要使用 YAML 文件,因为使用 properties 文件时顺序会丢失。

2、如下所示,表示路由匹配时优先级从上往下,对于符合 /myusers/** 规则的请求路径则匹配 users 微服务,即使后面还有 path 能匹配上,也不再生效。对于都未匹配上的请求,统统作为对微服务 legacy 的请求。

zuul:
  routes:
    users:
      path: /myusers/**
    zebra:
      path: /zebras/**
    legacy:
      path: /**

Zuul Http Client(客户端)

1、Zuul 使用的默认 HTTP 客户端现在由 Apache HTTP Client(而不是已弃用的 Ribbon RestClient)支持。

2、要使用 RestClient 或 okhttp3.OkHttpClient,则可以分别配置 ribbon.restclient.enabled = true 或 ribbon.okhttp.enabled = true。

3、如果要定制 Apache HTTP 客户端 或 OKHTTP 客户端,则可以提供类型为 ClosableHttpClient 或 OkHttpClient 的 bean。

官网 Zuul Http Client

Cookies and 敏感头信息

1、官网 Cookies and Sensitive Headers 总结起来就是:对于 Http 请求、响应头信息中的敏感信息可以使用 zuul.routes.*.sensitive-headers 属性进行过滤,无论是请求头信息、还是响应头信息,只要经过 zuul ,则都会被自动过滤删除。

2、sensitive-headers 的值是一个 set ,所有多个时可以用逗号隔开,它默认为:"Cookie", "Set-Cookie", "Authorization"。

Authorization:HTTP 授权的授权证书,如 Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== Cookie:HTTP 请求发送时,会把保存在该请求域名下的所有cookie值一起发送给 web 服务器。如 Cookie: $Version=1; Skin=new; Set-Cookie:服务端为客户端设置的 Cookie,如 Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1

3、下面通过示例进行演示:

3.1)zuul 网关应用 192.168.3.230:7790 的配置如下:

zuul:
  #routes 配置路由规则,值是一个 Map,key 是代理的微服务名称,如 snow-leopard,值仍然还可以使用 map
  routes:
    snow-leopard:         #自定义名称,整个 routes 下唯一即可
      path: /leopardApp/**         #代理路径
      #      serviceId: snow-leopard   #代理的微服务名称,当它有多个节点时,网关默认会使用 ribbon 进行负载均衡
      strip-prefix: true        #是否剥离/去掉前缀,默认为 true,表示实际转发路径会去掉 path 中的前缀 leopard
    ec-zebra:
      path: /zebra/**
      serviceId: ec-zebra
      strip-prefix: false     # false 表示不剥离 path 中的前缀 zebra,此时前缀会被当作真实的请求路径
      sensitive-headers: Cookie,Set-Cookie  #过滤传入、传出的敏感头信息

3.2)192.168.3.230:9394 微服务中提供如下方法:

@GetMapping("zebra/testSensitive")
public String testSensitive(HttpServletRequest request, HttpServletResponse response) {
    String cookie_header = request.getHeader("Cookie");
    System.out.println("cookie_header=" + cookie_header);

    Cookie cookie1 = new Cookie("token", "1582616408");
    response.addCookie(cookie1);

    return cookie1.toString();
}

3.3)测试结果:

http://192.168.3.230:9394/zebra/testSensitive:直接访问目标微服务时,未过滤 cookie,浏览器能收到后台返回的 cookie,后台也能取到前台传入的 cookie

http://192.168.3.230:7790/zebra/testSensitive:通过网关 zuul 访问微服务时,过滤 cookie,前台收不到后台设置的 cookie,后台自然也就取不到值。

测试动图:https://github.com/wangmaoxiong/Hystrix-hello-world/blob/master/images/1.gif

4、sensitive-headers 相当于头信息黑名单,默认值不为空,因此要想 Zuul 发送所有头信息,必须显式设置为空列表。如果要将 Cookie 或授权标头(Authorization) 传递到后端,则必须这样做:

 zuul:
  routes:
    users:   #users 微服务
      path: /myusers/**
      sensitiveHeaders:          #必须显示的设置为空,此时 "Cookie", "Set-Cookie", "Authorization" 等头信息才不会被过滤.
      url: https://downstream

5、zuul.routes.*.sensitive-headers 是对指定微服务设置敏感头信息,zuul.sensitive-headers 是全局设置敏感标头。局部的 sensitiveHeaders 会覆盖全局的 sensitiveHeaders 设置。如:

zuul:
  sensitive-headers: Cookie,Set-Cookie
  routes:
    users:
      path: /myusers/**

/routes & /filters 端点查看路由信息

官网 Management Endpoints

1、默认情况下,如果您将 @EnableZuulProxy 与 Spring Boot Actuator 结合使用,则会启用两个附加端点:routes 、filters

2、/routes 端点返回映射的路由列表:

get 方式请求时,还可以使用 /routes/details 查看路由的详细信息

post 方式请求时,会强制刷新现有路由信息(例如当服务目录中发生更改时)

3、/filters 端点通过 GET 方式请求,按类型返回 Zuul 过滤器的映射,对于 map 中的每种过滤器类型,您将获得该类型的所有过滤器的列表以及它们的详细信息。

4、因为 spring-cloud-starter-netflix-zuul 内部依赖了 spring-boot-starter-actuator 组件,所以无需再导入 actuator,只需要在配置文件中对外公开 /routes、/filters 端点即可。

#配置 actuator
management:
  endpoint:
    health:
      show-details: always #将端点细节展示给所有用户查看,默认为 never(不展示细节给所有用户)
  endpoints:
    web:
      exposure:
        include: health,info,hystrix.stream,routes,filters   #对外公开的端点,默认只公开了 health,info

源码:https://github.com/wangmaoxiong/Hystrix-hello-world

发布了456 篇原创文章 · 获赞 988 · 访问量 108万+

猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/104340965