SpringCloud 笔记4

前记

上一章了解断路器,这章将看看路由网关。

微服务

在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统。

简单的微服务系统

在 SpringCloud 微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(Zuul、Ngnix),再到达服务网关(Zuul集群),然后再到具体的服务。服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理,配置服务的配置文件放在Git仓库,方便开发人员随时改配置。

Zuul

Zuul 的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如 /api/user 转发到到 user 服务,/api/shop 转发到到 shop 服务。Zuul 默认和 Ribbon 结合实现了负载均衡的功能。

Zuul 功能:

  • Authentication
  • Insights
  • Stress Testing
  • Canary Testing
  • Dynamic Routing
  • Service Migration
  • Load Shedding
  • Security
  • Static Response handling
  • Active/Active traffic management

使用

新建一个 Module,命名为 zuulServer,添加依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
<dependencies>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.4.RELEASE</version>
</dependency>

</dependencies>

添加注解 @EnableZuulProxy 开启 Zuul 功能(作为服务,其本身也要向 Server 中心注册):

1
2
3
4
5
6
7
8
9
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class ZuulServiceApplication {

public static void main(String[] args) {
SpringApplication.run(ZuulServiceApplication.class, args);
}
}

配置文件 application.properties

1
2
3
4
5
6
7
8
9
10
11
12
server.port=10000

spring.application.name=zuulService

eureka.client.service-url.defaultZone=http://localhost:7000/eureka/

# 以 /api-a/ 开头的请求都转发给 ribbonConsumer 服务
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.service-id=ribbonConsumer
# 以 /api-b/ 开头的请求都转发给 feignConsumer 服务
zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.service-id=feignConsumer

运行情况

访问 api-a:

1
2
$ curl http://localhost:10000/api-a/add\?a\=2\&b\=91
Result: 93, from port: 8000

访问 api-b:

1
2
$ curl http://localhost:10000/api-b/add\?a\=2\&b\=91
Result: 93, from port: 8000

可以看到 Zuul 起到了路由作用。

服务过滤

Zuul 还有过滤,安全验证的功能。

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
50
51
52
53
54
55
56
57
58
59
60
61
@Component
public class MyFilter extends ZuulFilter {

private static Logger log = LoggerFactory.getLogger(MyFilter.class);

/**
* 不同生命周期的过滤器类型
* pre:在请求被路由之前调用
* routing:在路由请求时候被调用
* post: 在routing和error过滤器之后被调用
* error:处理请求时发生错误时被调用
*
* @return 生命周期
*/
@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();
log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());
// 获取 GET 参数 token
Object token = request.getParameter("token");
if (token == null) {
log.warn("token is empty");
// 过滤该请求,不往下级服务去转发请求,到此结束
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.setResponseBody("token is empty");
return null;
}
// 如果有token,则进行路由转发
log.info("token ok");
// 这里return的值没有意义,zuul框架没有使用该返回值
return null;
}
}

过滤器生命周期

测试没有 token 时:

1
2
3
4
5
6
$ curl -i http://localhost:10000/api-b/add\?a\=2\&b\=91
HTTP/1.1 401
Transfer-Encoding: chunked
Date: Mon, 28 May 2018 08:16:09 GMT

token is empty

测试有 token 时:

1
2
$ curl http://localhost:10000/api-b/add\?a\=2\&b\=91\&token\=123
Result: 93, from port: 8000

总结

  • 不仅仅实现了路由功能来屏蔽诸多服务细节,更实现了服务级别、均衡负载的路由。
  • 实现了接口权限校验与微服务业务逻辑的解耦。通过服务网关中的过滤器,在各生命周期中去校验请求的内容,将原本在对外服务层做的校验前移,保证了微服务的无状态性,同时降低了微服务的测试难度,让服务本身更集中关注业务逻辑的处理。
  • 实现了断路器,不会因为具体微服务的故障而导致服务网关的阻塞,依然可以对外服务。
Author

Zoctan

Posted on

2018-05-28

Updated on

2023-03-14

Licensed under