code.zip

写于:2019-01-02 00:02:37 更新于 2020-04-23

Zuul1.x版本 Github 仓库

一、Introduce

Spring Cloud 官方文档介绍

Routing in an integral part of a microservice architecture. For example, / may be mapped to your web application, /api/users is mapped to the user service and /api/shop is mapped to the shop service. Zuul is a JVM based router and server side load balancer by Netflix. ,

官方给出了对Zuul 的定义:路由器过滤器

通过路由的方式屏蔽微服务的细节,就像 使用 nginx 做转发服务一样的效果。

而过滤,就是在进入微服务访问之前,对来源请求 做定制化处理,例如对请求链接进行鉴权等。

网关所扮演的角色系统间的连接节点,或者说是系统的门户。

1.1、常见的使用场景

  • 监控
  • 测试
  • 权限校验
  • 活跃流量管理
  • 统一静态资源处理
  • 动态路由
  • 软负载均衡

1.2、项目引入 zuul 的好处

  • 作为统一的系统资源入口,屏蔽了所有微服务的细节,(类似于转发服务器的效果)
  • 与服务治理框架结合,实现自动化的服务实例维护,以及负载均衡的路由转发
  • 在鉴权实现中,能够实现与微服务业务逻辑的解耦
  • 网关中的过滤器能够在各种生命周期中去校验请求内容,将原本对外服务层做的校验前移。
  • 保证微服务的无状态性,同时降低微服务测试难度,让服务本身更关注业务逻辑。

二、Getting started-Route

2.1、直接配置 url 路由(只支持单实例服务)

port 备注
zuul 28080 网关服务
zuul-client 9527 服务

2.1.1、zuul 网关服务

  • step1、引入依赖:spring-cloud-starter-zuul
  • step2、启动类 追加 @EnableZuulProxy 注解

    1. @EnableZuulProxy // 开启 Zuul Api 网关服务功能
    2. @SpringBootApplication
    3. public class ZuulApplication {
    4. ......
    5. }
  • step3、配置文件 application.properties ```properties

    应用服务名称

    spring.application.name=zuul

端口号

server.port=28080

禁止所有服务的自动路由

zuul.ignored-services = *

传统的路由请求

zuul.routes.client.path = /zuul-client/** zuul.routes.client.url = http://localhost:9528

  1. <a name="cdebcaee"></a>
  2. #### 2.1.2、zuul-client 客户端服务
  3. - step1、提供测试 API(省略)
  4. - step2、配置 application.properties
  5. ```properties
  6. # applicationname
  7. spring.application.name = zuul-client
  8. # port
  9. server.port = 9527

2.2、通过 Ribbon 路由(支持多实例服务)

port 备注
zuul 28080 网关服务
zuul-client 9527 服务

zuul-client 同上,zuul 服务端修改配置如下

application.properteis 配置如下

  1. # 禁止所有服务的自动路由
  2. zuul.ignored-services = *
  3. ## zuul 配置
  4. zuul.routes.zuul-client.path = /zuul-client/**
  5. zuul.routes.zuul-client.sensitive-headers =
  6. zuul.routes.zuul-client.service-id = zuul-client
  7. ################################# Use Ribbon Without Eureka############
  8. ## 定义服务提供方 主机名,端口号,
  9. zuul-client.server.host= 127.0.0.1
  10. zuul-client.server.port= 9527
  11. ## 为 Ribbon 提供服务列表信息(RibbonLoadBlancerClient)
  12. zuul-client.ribbon.listOfServers=\
  13. http://${zuul-client.server.host}:${zuul-client.server.port}
  14. #########################################################################

2.3、通过 Eureka 路由(支持多实例服务)

port 备注
zuul 28080 网关服务
zuul-client 9527 服务
eureka 1111 注册中心

2.3.1、zuul 网关服务

  • step1、引入 zuul 和 eureka-client 依赖
  • step2、启动类追加 @EnableDiscoveryClient@EnableZuulProxy 注解
  • step3、配置 application.properties 如下
  1. ## zuul 配置
  2. zuul.routes.zuul-client.path = /zuul-client/**
  3. zuul.routes.zuul-client.sensitive-headers =
  4. zuul.routes.zuul-client.service-id = zuul-client
  5. # 禁止所有服务的自动路由
  6. zuul.ignored-services = *

2.3.2、eureka 服务

《Eureka Getting Started》

2.3.3、zuul-client 服务

  • step1、提供测试 API
  • step2、增加 eureka-client 依赖,并追加注解 @EnableDiscoveryClient

小贴士: 引入 Eureka 会针对所有注册到注册中心的服务实例生成默认路由,但是在实际生产中,zuul 网关并不需要针对所有的服务开发路由功能,这时候可以通过配置 zuul.ignored-services=* 忽略所有的默认路由,或者 zuul.ignored-services=${serverName}手动配置需要路由的路由信息,来实现按需路由。

三、Getting Started-Filter

Zuul 提供了 路由过滤器 两项功能。

Zuul 默认内置了许多的 Filter ,如下图:
01.png
所有 Fitler 的实现都继承自父类 ZuulFilter

ZuulFilter 类图结构如下
02.png
ZuulFilter 中关键的4个函数方法如下

  • filterType() ``` 过滤器类型,他决定过滤器在请求的那个声明周期中执行。

pre: 可以在请求被路由之前调用 routing:在路由请求时被调用 post在 routing 和 error 过滤之后被调用 error:在处理 请求时发生错误时被调用。

  1. - filterOrder()

过滤执行顺序,存在多个过滤器时,根据返回值,依次执行,数值越小优先级越高。

  1. - shouldFilter()

判定是否需要进行过滤拦截。

  1. - run()

真正执行的方法

  1. 实现一个自定义的 Filter 需要如下步骤:
  2. - step1、继承 ZuulFilter
  3. - step2、实现上述4个抽象函数
  4. - step3、注册到 IOC 容器中
  5. <a name="a6e9c37c"></a>
  6. ### 3.1、自定义 Filter实现鉴权
  7. > 通过 网关进行鉴权的优点:
  8. > - 减少代码冗余
  9. > - 统一处理,升级修改简便
  10. > - 在请求转发前,进行鉴权,将无效请求拦截,节省资源开销
  11. <a name="83175ad0"></a>
  12. #### 代码实现
  13. ```java
  14. /**
  15. * @Description 权限拦截器,如果参数中携带有 token则放过,如果没有,返回 401
  16. */
  17. @Component
  18. public class AccessFilter extends ZuulFilter {
  19. @Autowired
  20. private Environment environment;
  21. // 过滤器类型,他决定过滤器在请求的那个声明周期中执行。
  22. @Override
  23. public String filterType() {
  24. return "pre";
  25. }
  26. // 过滤执行顺序,存在多个过滤器时,根据返回值,依次执行,数值越小优先级越高。
  27. @Override
  28. public int filterOrder() {
  29. return 0;
  30. }
  31. // 判定是否需要进行过滤拦截。
  32. @Override
  33. public boolean shouldFilter() {
  34. String ignoreUrl = environment.getProperty("zuul.ignore-url");
  35. String[] ingoreUrls = ignoreUrl.split(",");
  36. RequestContext ctx = RequestContext.getCurrentContext();
  37. HttpServletRequest request = ctx.getRequest();
  38. for(int i = 0;i < ingoreUrls.length; i++){
  39. if(request.getServletPath().contains(ingoreUrls[i])){
  40. return false;
  41. }
  42. }
  43. return true;
  44. }
  45. @Override
  46. public Object run() throws ZuulException {
  47. RequestContext ctx = RequestContext.getCurrentContext();
  48. HttpServletRequest request = ctx.getRequest();
  49. System.err.println(request.getServletPath());
  50. String token = request.getParameter("token");
  51. if(null == token || token.trim().equals("")){
  52. ctx.setSendZuulResponse(false); // 设定不进行路由
  53. ctx.setResponseStatusCode(401); // 设定返回码
  54. HttpServletResponse response = ctx.getResponse();
  55. ctx.setResponseBody("401 no auth"); // 设定返回内容
  56. return null;
  57. }
  58. return null;
  59. }
  60. }

小贴士: Zuul 能够对指定的过滤器进行关闭操作,相关配置如下

  1. ## 关闭 指定 过滤器的指定周期的拦截器
  2. ## 规则:zuul.<SimpleClassName>.<filterType>.disable=true
  3. zuul.AccessFilter.pre.disable=true

四、Zuul Actuator 端点

《Actuator 应用监控》

Zuul 提供有相关的 Actuator 监控端点,实现类如下
03.png
相关端点访问测试如下:

路由信息 /routes

  1. {
  2. "/client/**": "client",
  3. "/provider/**": "provider"
  4. }

过滤器信息 /filters

  1. {
  2. "error": [
  3. {
  4. "class": "org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter",
  5. "order": 0,
  6. "disabled": false,
  7. "static": true
  8. }
  9. ],
  10. "post": [
  11. {
  12. "class": "org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter",
  13. "order": 1000,
  14. "disabled": false,
  15. "static": true
  16. }
  17. ],
  18. "pre": [
  19. {
  20. "class": "org.springframework.cloud.netflix.zuul.filters.pre.DebugFilter",
  21. "order": 1,
  22. "disabled": false,
  23. "static": true
  24. }
  25. ],
  26. "route": [
  27. {
  28. "class": "org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter",
  29. "order": 100,
  30. "disabled": false,
  31. "static": true
  32. }
  33. ]
  34. }

五、Zuul HTTP Client

Spring Cloud 官方文档介绍

The default HTTP client used by zuul is now backed by the Apache HTTP Client instead of the deprecated Ribbon RestClient. To use RestClient or to use the okhttp3.OkHttpClient set ribbon.restclient.enabled=true or ribbon.okhttp.enabled=true respectively. If you would like to customize the Apache HTTP client or the OK HTTP client provide a bean of type ClosableHttpClient or OkHttpClient.

zuul 默认使用的是 Apache HTTP Client 进行客户端请求。且又提供了两种实现方式: HttpClient 和 OkHttpClient。

执行时机为在进行调用服务的时候触发(验证:在HttpClientRibbonConfiguration httpClient(ApacheHttpClientFactory httpClientFactory,HttpClientConnectionManager connectionManager, IClientConfig config) 打个断点,然后启动 api-gateeway 服务,调用 client 接口信息,进行测试)

默认 HTTPClient

  1. @Configuration
  2. @ConditionalOnClass(name = "org.apache.http.client.HttpClient")
  3. @ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true)
  4. public class HttpClientRibbonConfiguration {
  5. @Value("${ribbon.client.name}")
  6. private String name = "client";
  7. .................................................

@ConditionalOnProperty(name = “ribbon.httpclient.enabled”, matchIfMissing = true) 得知

切换 HTTPClient

  1. ribbon.restclient.enabled=true
  2. ribbon.okhttp.enabled=true