一、关于
Zuul是netflix开源的一个网关服务,提供动态路由、监视、弹性、安全性等功能。
本文代码仓库地址:https://github.com/lazyrabb1t/rabb-springcloud-demo
二、使用Zuul
1、创建zuul模块
2、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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-netflix-zuul</artifactId>
</dependency>
3、配置
server:
port: 15000
spring:
application:
name: rabb-zuul
eureka:
client:
service-url:
defaultZone: http://localhost:10002/eureka/
4、启动类添加注解@EnableZuulProxy开启zool网关
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
5、启动eureka-server、provider和当前模块进行测试
zuul实现了路由的默认配置,自动配置的路由以服务名称为匹配路径,相当于如下配置:
zuul:
routes:
rabb-eureka-provider:
path: /rabb-eureka-provider/**
访问下面的接口,就可以直接访问provider模块的接口
http://localhost:15000/rabb-eureka-provider/
三、路由配置
1、自定义路由配置
在application.yml里可以配置服务的路径,将匹配的请求路由到指定的服务,如下将/provider/的请求路由到rabb-eureka-provider进行处理:
zuul:
routes:
rabb-eureka-provider:
path: /provider/**
2、默认路由
若不进行配置,则zuul会添加默认的路由规则,自动已注册的服务名称为路径前缀进行匹配,如下:
zuul:
routes:
rabb-eureka-provider:
path: /rabb-eureka-provider/**
若不想使用默认路由,则可以添加如下配置:
zuul:
ignored-services: rabb-eureka-provider
3、添加访问前缀
另外可以给接口添加统一前缀,如下:
zuul:
prefix: /zuul
4、添加监控
使用SpringBoot Actuator可以查看路由的信息,在引入其依赖后,作如下配置:
management:
endpoints:
web:
exposure:
include: 'routes'
访问下面的两个路径,就可以看到路由的信息了
http://localhost:15000/actuator/routes
http://localhost:15000/actuator/routes/details
四、过滤器
1、Zuul过滤器
Zuul中有以下几种典型的过滤器类型。
- pre:在请求被路由到目标服务前执行,比如权限校验、打印日志等功能;
- routing:在请求被路由到目标服务时执行,这是使用Apache HttpClient或Netflix Ribbon构建和发送原始HTTP请求的地方;
- post:在请求被路由到目标服务后执行,比如给目标服务的响应添加头信息,收集统计数据等功能;
- error:请求在其他阶段发生错误时执行。
2、自定义过滤器
这里继承ZuulFilter实现一个token校验的过滤器
@Component
@Slf4j
public class AuthFilter extends ZuulFilter {
/**
* 过滤器类型,有pre、routing、post、error四种。
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器执行顺序,数值越小优先级越高。
*/
@Override
public int filterOrder() {
return 1;
}
/**
* 是否进行过滤,返回true会执行过滤。
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 自定义的过滤器逻辑,当shouldFilter()返回true时会执行。
*/
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String host = request.getRemoteHost();
String method = request.getMethod();
String uri = request.getRequestURI();
log.info("Remote host:{},method:{},uri:{}", host, method, uri);
String token = request.getParameter("token");
if (StringUtils.isBlank(token)) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
requestContext.setResponseBody("请携带token进行访问");
requestContext.addZuulResponseHeader("content-type", "text/html;charset=utf-8");
}
return null;
}
}
五、负载均衡
Zuul默认支持负载均衡,可以通过下面的方式来注册要使用的策略类型
@Bean
public IRule loadBalancerRule() {
return new RandomRule();
}
六、服务降级
可以通过实现FallbackProvider类来完成服务降级
@Component
@Slf4j
public class RabbFallback implements FallbackProvider {
/**
* 指定要降级的微服务名称
*
* @return
*/
@Override
public String getRoute() {
// 对所有微服务降级
return "*";
// 仅对指定的微服务进行降级
// return "rabb-eureka-provider";
}
/**
* 降级方法
*
* @param route
* @param cause
* @return
*/
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
log.info("服务降级,route:{},cause:{}", route, cause == null ? "" : cause.getMessage());
return new ClientHttpResponse() {
@Override
public HttpHeaders getHeaders() {
// 设置降级响应头信息
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
@Override
public InputStream getBody() throws IOException {
// 设置降级信息
// String msg = "fallback:" + ConsumerFallback.this.getRoute();
String msg = "fallback:" + route;
return new ByteArrayInputStream(msg.getBytes());
}
@Override
public HttpStatus getStatusCode() throws IOException {
// 返回状态常量
return HttpStatus.SERVICE_UNAVAILABLE;
}
@Override
public int getRawStatusCode() throws IOException {
// 返回状态码,这里为503
return HttpStatus.SERVICE_UNAVAILABLE.value();
}
@Override
public String getStatusText() throws IOException {
// 返回状态码对应的状态短语,这里为"Service Unavailable"
return HttpStatus.SERVICE_UNAVAILABLE.getReasonPhrase();
}
@Override
public void close() {
}
};
}
}