微服务学习之——SpringCloud-Zuul
文章目录
前言
网关介绍
在微服务架构中,服务节点数量较多,对于客户端而言,多个服务节点暴露出来的 API 应该是统一的,否则每个节点
地址不同,客户端就需要维护所有服务节点地址然后再选择一个访问,很明显客户端的维护成本就很高。
此时就需要有一个暴露统一 API 的网关服务将多服务节点封装,客户端访问网关,网关再将客户端的请求分发给被封
装的服务节点,这样就避免了客户端和服务端的直接交互。
一、网关是什么?
顾名思义,是一个网络关口、通道,是整个微服务平台所有请求的唯一入口,所有的客户端和消费端都通过统
一的网关接入微服务,在网关层处理所有的非业务功能。
而Zuul 是 Netflix 开源的微服务网关,它可以和 Eureka、Ribbon、Hystrix 等组件配合使用。Zuul的核心是一系列的
过滤器。
二、功能职责
- 身份认证与安全: 识别每个资源的验证要求,并拒绝与要求不符的请求。
- 动态路由: 动态的将请求路由到不同的后端集群。
- 审查与监控: 边缘位置追踪有意义的数据和统计结果,从而带来精确的生产视图。
- 压力测试: 逐渐增加指向集群的流量,以了解性能。
- 负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
- 静态响应处理: 在边缘位置直接建立部分响应,从而避免其转发到内部集群。
- 多区域弹性: 跨越 AWS Region进行请求路由,从旨在实现 ELB(Elastic Load Balancing)使用的多样化,以
及让系统的边缘更贴近系统的使用者。
三、网关架构图
不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过Zuul这个网关,然后
再由网关来实现 鉴权、动态路由等等操作。Zuul就是我们服务的统一入口。
四、使用步骤
1.静态路由
1.引入依赖
代码如下(示例):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
2.编写启动类
通过 @EnableZuulProxy 注解开启Zuul的功能。
代码如下(示例):
@SpringBootApplication
@EnableZuulProxy //开启Zuul的网关功能
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class);
}
}
3. 编写配置
server:
port: 10011
spring:
application:
name: api-app
# 配置路由规则
zuul:
routes:
user-service: # 路由唯一id,随意指定
path: /user-service/** # 映射服务路径
url: http://localhost:8081 # 映射服务路径对应的服务实际url地址
符合 path 路由匹配规则的一切请求,都将路由到 url 指定的地址。
4. 测试
访问的路径中需要加上配置规则的映射路径,访问:http://localhost:10011/user-service/user/info/2
在刚才的路由规则中,我们把路径对应的服务地址写死了!如果同一服务有多个实例的话,这样做显然就不合理了,所以下面就介绍另一种写法。
2.动态路由
我们应该根据服务的名称,去Eureka注册中心查找服务对应的所有实例列表,然后进行动态路由才对!
1. 引入 Eureka(服务注册中心)依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.编写启动类
通过@EnableEurekaClient 注解开启Eureka
@SpringBootApplication
@EnableZuulProxy //开启Zuul的网关功能
@EnableEurekaClient //启用Eureka客户端
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class);
}
}
3. 修改配置文件
server:
port: 10011#网关端口
spring:
application:
name: api-app
# eureka配置
eureka:
client:
service-url:
defaultZone: http://localhost:10010/eureka/
# 配置路由规则
zuul:
routes:
user-service: # 路由唯一id,随意指定
path: /user-service/** # 映射服务路径
serviceId: user-app # 指定服务名称
因为已经有了 Eureka 客户端,我们可以从 Eureka 获取服务的地址信息,因此映射时无需指定 IP 地址,而是通过服务名称来访问,而且 Zuul 已经集成了 Ribbon 的负载均衡功能。
4. 测试
这里当发出请求时,路由拦截,规则匹配成功后这里不再是直接跳地址,而是根据服务名称通过服务注册中心拿出服务地址,然后再根据地址进行跳转。解决了同一服务多个实例,也就是多个地址的问题,并且这样保证了服务的安全性,因为不知道具体地址。
3.简化路由配置
这里我们还可以简化路由写法
在刚才的配置中,规则是这样的:
zuul:
routes:
user-service: # 路由唯一id,随意指定
path: /user-service/** # 映射服务路径
serviceId: user-app # 指定服务名称
现在可以改成:
zuul:
routes:
user-app: /user-service/** # 这里是映射路径
这里我门直接把要跳转的服务名代替路由id后面写上路由匹配规则,省去了对服务名称的配置。
五.路由简单使用介绍
1.默认路由规则
在使用Zuul的过程中,上面讲述的规则已经大大的简化了配置项。但是当服务较多时,配置也是比较繁琐的。因此
Zuul就指定了默认的路由规则:
- 默认情况下,一切服务的映射路径就是服务名本身。
- 例如服务名为: user-app ,则默认的映射路径就是: /user-app/**
也就是说,之前上面介绍的的映射规则我们完全不用配置,因为它就是默认路由规则
2.路由前缀
这里说一下,路由规则用于在微服务项目内匹配服务,路由前缀用于标识不同微服务项目之间的地址
server:
port: 10011
spring:
application:
name: api-app
# eureka配置
eureka:
client:
service-url:
defaultZone: http://localhost:10010/eureka/
# 配置路由规则
zuul:
routes:
user-app: /user-service/** # 简化配置
prefix: /api # 路由前缀
在发起请求时,路径就要以 /api 开头来寻找当前微服务项目,路径 /api/user-service/user/info/2 将会被代理到 /user-service/user/info/2
3.过滤器
Zuul作为网关的其中另一个重要功能,就是实现请求的鉴权。可以通过Zuul提供的过滤器来实现的。
1.**ZuulFilter类:**是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法
shouldFilter()
:返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false不执行。run()
:过滤器的具体业务逻辑。filterType()
:返回字符串,代表过滤器的类型。包含以下4种:- pre :请求在被路由之前执行
- routing :在路由请求时调用
- post :在routing和errror过滤器之后调用
- error :处理请求时发生错误调用
filterOrder()
:通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。
2.Zuul四种过滤器
- PRE 过滤器: 请求路由到具体的服务之前执行,可以用作安全校验、身份校验、参数校验等前置工作。
- ROUTING 过滤器: 用于将请求路由到具体的服务实例,默认使用 Http Client进行网络请求。
- POST 过滤器: 在请求已经被路由到微服务后执行,通常用于收集统计信息、指标并将响应返回给客户端。
- ERROR 过滤器: 在其他过滤器出现异常时执行。
3.过滤器执行生命周期
-
**工作流程:**Zuul本质是一个 Servlet 来对请求进行控制,核心是创建了一系列的过滤器。
-
正常流程:
- 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,
执行请求,返回结果后,会到达post过滤器。而后返回响应。
- 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,
-
异常流程:
- 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给post过滤器,最后返回给用户。
- 如果是error过滤器自己出现异常,最终也会进入post过滤器,而后返回。
- 如果是post过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的请 时,求不会再到达post过滤器了。
4.使用场景
- 请求鉴权: 一般放在pre类型,如果发现没有访问权限,直接就拦截。
- 异常处理: 一般会在error类型和post类型过滤器中结合来处理。
- 服务调用时长统计: pre和post结合使用。
5.自定义过滤器
案例要求: 模拟一个登录的校验。基本逻辑:如果请求中有access-token参数,则认为请求有效,放行。
@Component
public class LoginFilter extends ZuulFilter {
@Override
public String filterType() {
// 登录校验,设置为前置拦截
return "pre";
}
@Override
public int filterOrder() { // 过滤器执行顺序设置为1
return 1;
}
@Override
public boolean shouldFilter() {
// 返回true,代表过滤器生效。
return true;
}
@Override
public Object run() throws ZuulException {
//获取上下文
RequestContext currentContext = RequestContext.getCurrentContext();
//获取请求
HttpServletRequest request = currentContext.getRequest();
//获取token
String token = request.getHeader("token");
//判断token
if (token == null || "".equals(token.trim())) {
//请求不继续转发到后端服务
currentContext.setSendZuulResponse(false);
HttpServletResponse response = currentContext.getResponse();
//设置MIME类型(application/json;charset=UTF-8)
response.setContentType("application/json;charset=UTF-8");
//返回的提示信息
String result =
JSON.toJSONString(ResponseApi.setFail(ExceptionEnum.USER_NONE_LOGIN.code(),
ExceptionEnum.USER_NONE_LOGIN.message()));
//设置响应体
currentContext.setResponseBody(result);
}
return null;
}
}
测试:
-
没有token参数时,访问失败:
-
携带token参数后:
6.负载均衡和熔断
Zuul中默认就已经集成了Ribbon负载均衡和Hystix熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。因此建议我们手动进行配置:
zuul:
retryable: true
ribbon:
ConnectTimeout: 250 # 连接超时时间(ms)
ReadTimeout: 2000 # 通信超时时间(ms)
OkToRetryOnAllOperations: true # 是否对所有操作重试
MaxAutoRetriesNextServer: 2 # 同一服务不同实例的重试次数
MaxAutoRetries: 1 # 同一实例的重试次数
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMillisecond: 6000 # 熔断超时时长:6000ms
总结
具体来说,Zuul通过配置不同的路由规则,将来自外部客户端的请求转发到不同的后端微服务上使得客户端看到的是一个整体的API接口。同时,Zuul也可以对请求进行限流、防火墙、负载均衡等操作,在保证系统安全和稳定性的同时提高服务效率。总之,Zuul与路由密不可分,是微服务架构中必不可少的组件之一。