# 154. 商品详情页动态渲染系统:Spring Cloud 之 Zuul 网关路由

本章讲解的就是官网教程 (opens new window)中的入门部分。

常规的 spring cloud 的微服务架构下,前端请求先通过 nginx 走到 zuul 网关服务, zuul 负责路由转发、请求过滤等网关接入层的功能,默认和 ribbon 整合实现了负载均衡

比如说你有 20 个服务暴露出去,你的调用方如果要跟 20 个服务打交道,是不是很麻烦? 所以比较好的一个方式,就是开发一个通用的 zuul 路由转发的服务,根据请求 api 模式,动态将请求路由转发到对应的服务

你的前端,主要考虑跟一个服务打交道就可以了

# 整合 zuul

创建一个新项目 zuul-server/build.gradle

plugins {
    id 'org.springframework.boot' version '2.1.6.RELEASE'
    id 'java'
}

apply plugin: 'io.spring.dependency-management'

ext {
    set('springCloudVersion', "Greenwich.SR2")
}

dependencies {
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-zuul' // 引入 zuul 依赖
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}


dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

使用注解开启

@EnableZuulProxy
1

配置路由的两个服务,application.yml

server:
  port: 9010

spring:
  application:
    name: zuul-server

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

zuul:
  routes:
    greeting-service:
      path: /greeting-service/**
      serviceId: greeting-service
    eshop-eurela-client:
      path: /eshop-eurela-client/**
      serviceId: eshop-eurela-client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

测试是否转发成功:

  • 现在访问地址:http://localhost:9010/greeting-service/ 之前要访问:http://localhost:9001
  • 现在访问地址:http://localhost:9010/eshop-eurela-client/ 之前要访问:http://localhost:9000

可以看到可以被正常转发

# 请求过滤

@Component
public class MyZuulFilter extends ZuulFilter {

    private static Logger logger = LoggerFactory.getLogger(MyZuulFilter.class);

    // 过滤器类型 pre,routing,post,error
    @Override
    public String filterType() {
        return "pre";
    }

    // 顺序
    @Override
    public int filterOrder() {
        return 0;
    }

    // 根据逻辑判断是否要过滤
    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        logger.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));

        Object userId = request.getParameter("userId");

        // 不携带 userId 这个参数就表示为未登陆
        if (userId == null) {
            logger.warn("userId is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("userId is empty");
            } catch (Exception e) {
            }

            return null;
        }

        logger.info("ok");

        return null;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  • 再次访问地址:http://localhost:9010/eshop-eurela-client/ ,会发现返回了 401 的错误:userId is empty
  • 只能加上 userId 访问:http://localhost:9010/eshop-eurela-client/?userId=12