SpringCloud系列之Zuul自定义

作者 胡萝虎 日期 2018-05-25
SpringCloud系列之Zuul自定义

SpringCloud系列之Zuul中我们介绍了Zuul与Spring Cloud整合的应用以及简单配置。Zuul是基于Filter的,通过组合不同的Filter可以实现不同的功能,Spring Cloud整合Zuul后也提供了一些默认的FIlter,如DebugFilter、FormBodyWrapPreFilter、PreDecorationFilter等。实际上,我们也可以编写自己的Filter来实现相应的功能。同时,在Spring Cloud中Zuul通过整合Hystrix也可以实现容错和断路保护。

Filter

Filter是Zuul的核心组件,不同类型的Filter作用在一个请求的不同生命周期上,具体如下:

  • PRE

    此类型Filter在请求被路由之前调用。可以应用在身份认证、安全检查、负载均衡等等

  • ROUTING

    此类型Filter实际上就是路由器,用于将请求路由到代理的服务上

  • POST

    此类型Filter在请求被路由到服务后调用,可以用于收集性能指标、增强响应等

  • ERROR

    此类型Filter在其他阶段发送错误时被调用,可以用于记录日志、处理错误等

  • 自定义

    除了上面默认的几种Filter,我们也可以自定义类型

自定义Filter

复制gateway-zuul,改名为gateway-zuul-filter

创建Filter

自定义Filter需要继承ZuulFilter抽象类

package com.huluohu.cloud.gateway.infrastructure.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;

@Slf4j
public class PreRequestLogFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre"; //filter类型
}

@Override
public int filterOrder() {
return 1; //执行顺序
}

@Override
public boolean shouldFilter() {
return true; //是否执行
}

//具体逻辑
@Override
public Object run() {
final RequestContext context = RequestContext.getCurrentContext();
final HttpServletRequest request = context.getRequest();

log.info(String.format("send %s request to %s",request.getMethod(),request.getRequestURL().toString()));
return null;
}
}
  • 创建一个类,继承ZuulFilter
  • 设置Filter类型、执行顺序和是否执行
  • 编写具体业务逻辑(打印请求日志)

配置

在Configuration类中配置自定义Filter

package com.huluohu.cloud.gateway;

import com.huluohu.cloud.gateway.infrastructure.filter.PreRequestLogFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ApplicationConfiguration {
@Bean
public PreRequestLogFilter preRequestLogFilter() {
return new PreRequestLogFilter();
}
}

测试

  1. 启动Eureka Server(local profile)
  2. 启动Config Server(不需要区分profile)
  3. 启动provider-user(local profile)
  4. 启动consumer-movie-hystrix-mq(local profile)
  5. 启动gateway-zuul-filter(local profile)
  • 启动应用列表

  • 在浏览器中请求http://localhost:6999/movie/test/user/1

    可以看到控制台打印了日志

容错与断路保护

Zuul默认集成了Hystrix,支持容错和断路保护,所以也可以通过Turbine对Hystrix监控数据进行聚合。

Zuul中的Hystrix监控不同于普通服务的监控,它的粒度是服务级别的,而不是API级别。

在Zuul中,回退逻辑需要进行自定义,即实现类实现FallbackProvider接口

自定义回退逻辑

package com.huluohu.cloud.gateway.infrastructure.fallback;

import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;

@Component
public class MovieServiceFallbackProvider implements FallbackProvider {
@Override
public ClientHttpResponse fallbackResponse(Throwable cause) {
return new MovieFallbackResponse(cause.getLocalizedMessage());
}

@Override
public String getRoute() {
return "consumer-movie";
}

@Override
public ClientHttpResponse fallbackResponse() {
return new MovieFallbackResponse(null);
}


private class MovieFallbackResponse implements ClientHttpResponse {
private String cause;

public MovieFallbackResponse(String cause) {
this.cause = cause;
}

@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}

@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.OK.value();
}

@Override
public String getStatusText() throws IOException {
return HttpStatus.OK.getReasonPhrase();
}

@Override
public void close() {

}

@Override
public InputStream getBody() throws IOException {
final StringBuilder builder = new StringBuilder();
builder.append("Movie 服务不可用");
if (StringUtils.isNotBlank(this.cause)) {
builder.append("(" + cause + ")");
}
builder.append(",请稍后再试。");
return new ByteArrayInputStream(builder.toString().getBytes());
}

@Override
public HttpHeaders getHeaders() {
final HttpHeaders headers = new HttpHeaders();
final MediaType mediaType = new MediaType("application", "json", Charset.forName("UTF-8"));
headers.setContentType(mediaType);
return headers;
}
}
}

测试

  • 关闭consumer-movie-hystrix-mq

  • 浏览器中请求http://localhost:6999/movie/test/user/1

    • 错误响应1

    • 错误响应2

  • 多次请求zuul,此时可以看到Hystrix监控到了失败情况(失败数量、断路器开启)

你可能会喜欢

“扫一扫接着看”