《SpringCloud微服务 六》之 Zuul 网关路由
目录
六、Zuul网关路由
Zuul是netflix组的一个子项目
之前学习了spring cloud提供给微服务系统的服务注册与发现、配置中心统一管理、断路器、负载均衡、服务间通信工具等,而Netflix提供了一个组件Zuul,它的作用有微服务网关,提供动态路由,访问过滤等服务
Zuul组件的核心就是一系列的过滤器,这些过滤器有:
1.身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求
2.审查与监控:指标健康监控
3.动态路由:动态将请求路由到不同后端集群
4.压力测试:逐渐增加指向集群的流量,以了解性能
5.负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
6.静态响应处理:边缘位置进行响应,避免转发到内部集群
没有网关路由之前:
比如目前有两个服务:books-service、user-service
分别对应的有个资源请求需要对外提供的,比如:/books、/users
若是没有网关路由的话,需要请求:http://books-service/books、http://users-service/users
服务消费者方需要维护两个服务的地址,若是现在需要加上认证,服务生产方需要对每个服务都需要添加认证的代码块,双方都比较头疼麻烦
若是现在有了网关路由,则统一对外管理API
比如 :
请求网关服务:http://zuul-server/books-service/books zuul服务就转发请求到 books-service 服务进行查询
请求网关服务:http://zuul-server/user-service/users zuul服务就转发请求到 user-service 服务进行查询
若是需要统一添加认证,则在zuul服务中拦截一切请求,通过认证的进行转发,没有通过认证的,则直接返回,这样减少了多个微服务直接的服务IP暴露,并且外部是不需要知道内部微服务间通信的
6.1 配置服务路由
1.添加依赖
<!-- Netflix Zuul网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
2.启动类上添加开启路由代理注解 @EnableZuulProxy
// 开启网关路由代理
@EnableZuulProxy
@SpringBootApplication
public class LearnZuulApplication {
public static void main(String[] args) {
SpringApplication.run(LearnZuulApplication.class, args);
}
}
6.1.1 URL路径匹配方式
URL路径匹配方式设置路由
server:
port: 5050
servlet:
context-path: /
spring:
application:
name: zuul-server
# 配置服务路由
zuul:
routes:
# 服务名称(可以自定义 可以配置多个服务,名称不能相同)
books-service:
path: /book-server/** #接口前缀
url: http://localhost:9090/ #实际服务地址
# 用户服务
users-service:
path: /users-server/**
url: http://localhost:9091/
zuul-server服务启动后,充当微服务门户,当所有访问请求URL以 /book-server/** 开头的,都会被zuul路由到 http://localhost:9090/ 服务去实际访问
比如:
启动 books-service 服务,有个接口 /book,返回值
{"id":1002,"name":"疯狂Java","author":"李刚"}
当访问:http://localhost:5050/book-server/book 时,会被zuul重定向到books-service服务,请求 /book 接口
其实会发现,books-service服务的地址 http://localhost:9090 在eureka服务注册中心里面已经有了,所以可以结合 eureka服务注册中心更加方便的获取到服务地址
6.1.2 服务ID匹配方式
结合eureka服务注册中心使用
首先 books-service服务 要注册到eureka服务中心里,serviceID= books-service
zuul路由项目注册eureka服务注册中心上:
1.添加eureka客户端依赖
<!-- eureka客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.启动类上添加服务注册注解
// 开启网关路由代理
@EnableZuulProxy
// 启动eureka客户端
@EnableEurekaClient
@SpringBootApplication
public class LearnZuulApplication {
public static void main(String[] args) {
SpringApplication.run(LearnZuulApplication.class, args);
}
}
3.添加路由配置
server:
port: 5050
servlet:
context-path: /
spring:
application:
name: zuul-server
# eureka服务中心配置
eureka:
client:
service-url:
defaultZone: http://localhost:8080/eureka
# 配置服务路由
zuul:
routes:
# 书籍服务(名称可自定义)
books-service:
#请求前缀
path: /book-server/**
#eureka服务中心中的服务ID,zuul可以根据eureka中的服务地址路由到具体服务
serviceId: books-service
当访问:http://localhost:5050/book-server/book 时,zuul网关会根据serviceId去找eureka服务中心,然后拿到 books-service服务的具体地址,然后执行 /book 接口
{"id":1002,"name":"疯狂Java","author":"李刚"}
其实会发现,books-service服务的serviceId也在eureka服务注册中心里面配置,所以 zuul网关路由默认支持直接去eureka配置中心获取服务,进行路由
6.1.3 默认读取eureka进行路由
比如:什么都没有配置路由规则的前提下
默认路由规则:http://zuulIP:zuulPort/serviceId/path
访问:http://localhost:5050/books-service/book 时,zuul会根据规则 http://zuulIP:zuulPort/serviceId/path 获取到serviceID,然后去eureka注册中心获取到服务的地址,进行访问
6.2 自定义过滤器
zuul网关允许用户自定义过滤器进行统一的配置,比如:用户认证token、日志记录、权限校验等
zuul提供了一个过滤器抽象类 com.netflix.zuul.ZuulFilter
只要继承此类,自定义实现四个方法即可:
1.public boolean shouldFilter() 是否执行此过滤器
2.public String filterType() 过滤器类型
3.public int filterOrder() 过滤器顺序(过滤器执行顺序,数字越小,优先级越高)
4.Object run() throws ZuulException 过滤器执行体
filterType:过滤器类型有四种
1.pre - 前置过滤器,在请求被zuul路由到真正服务前执行,通常用于处理身份认证,日志记录等
2.route - 在路由执行后,服务调用前被调用,通常用于特定服务调用前添加参数等
3.error - 任意一个filter发生异常的时候执行或远程服务调用没有反馈的时候执行(超时),通常用于处理异常
4.post - 在route或error执行后被调用,一般用于收集服务信息,统计服务性能指标等,也可以对response结果做特殊处理
6.2.1 自定义权限token校验过滤器
自定义权限校验token的过滤器
package com.tianya.springcloud.zuul.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
/**
* @author TianwYam
* @description 自定义filter 拦截网关服务
* @date 2022年3月2日下午6:40:19
*/
@Slf4j
@Component
public class AuthFilter extends ZuulFilter {
@Override
public boolean shouldFilter() {
// 是否执行该过滤器
return true;
}
/**
* 具体执行方法
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
// 获取请求
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info("当前执行方法:{},{}", request.getMethod(),
request.getRequestURL().toString());
// 获取 token header(简单校验,具体业务具体分析)
String token = request.getHeader("token");
if (StringUtils.hasText(token)) {
// 对请求放行 继续路由
ctx.setSendZuulResponse(true);
ctx.setResponseStatusCode(200);
return null;
}
// 不对其路由
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(403);
ctx.setResponseBody("权限不对,token不能为空");
// 取response 设置返回格式
HttpServletResponse response = ctx.getResponse();
// 防止界面展示乱码
response.setContentType("text/html;charset=UTF-8");
return null;
}
@Override
public String filterType() {
// 过滤器类型(路由前执行)
return "pre";
}
@Override
public int filterOrder() {
// 过滤器执行顺序,数字越小,优先级越高
return 0;
}
}
启动网关服务后,直接访问:http://localhost:5050/book-server/book 界面展示:权限不对,token不能为空(因为我们header里面没有token)