Spring Cloud 微服务架构搭建(五)— Netflix Zuul 路由网关及服务过滤

本文首发于:https://antoniopeng.com

Zuul 简介

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

引入依赖

pom.xml 中主要添加 spring-cloud-starter-netflix-eureka-serverspring-cloud-starter-netflix-zuul 依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

相关配置

application.yml 中主要添加 Zuul 路由配置

zuul:
  routes:
    api-a:
      path: /api/ribbon/**
      serviceId: hello-spring-cloud-web-admin-ribbon
    api-b:
      path: /api/feign/**
      serviceId: hello-spring-cloud-web-admin-feign

路由说明:

  • 以 /api/ribbon 开头的请求都转发给 spring-cloud-web-admin-ribbon 服务
  • 以 /api/feign 开头的请求都转发给 spring-cloud-web-admin-feign 服务

Application 入口类中添加 @EnableZuulProxy 注解开启 zuul 功能

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }
}

配置网关路由失败时的回调

创建 WebAdminFeignFallbackProvider 回调类

/** * 路由 hello-spring-cloud-web-admin-feign 失败时的回调 */
@Component
public class WebAdminFeignFallbackProvider implements FallbackProvider {

    @Override
    public String getRoute() {
        // ServiceId,如果需要所有调用都支持回退,则 return "*" 或 return null
        return "hello-spring-cloud-web-admin-feign";
    }

    /** * 如果请求服务失败,则返回指定的信息给调用者 * @param route * @param cause * @return */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            /** * 网关向 api 服务请求失败了,但是消费者客户端向网关发起的请求是成功的, * 不应该把 api 的 404,500 等问题抛给客户端 * 网关和 api 服务集群对于客户端来说是黑盒 * @return * @throws IOException */
            @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 {
                ObjectMapper objectMapper = new ObjectMapper();
                Map<String, Object> map = new HashMap<>();
                map.put("status", 200);
                map.put("message", "无法连接");
                return new ByteArrayInputStream(objectMapper.writeValueAsString(map).getBytes("UTF-8"));
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                // 和 getBody 中的内容编码一致
                headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
                return headers;
            }
        };
    }
}

测试路由访问

依次运行 EurekaApplication > ServiceAdminApplication > WebAdminRibbonApplication > WebAdminFeignApplication > ZuulApplication 各服务

使用 Zuul 的服务过滤功能

Zuul 不仅仅只是路由,还有很多强大的功能。比如用在安全验证方面。

创建服务过滤器

/** * Zuul 的服务过滤演示 */
@Component
public class LoginFilter extends ZuulFilter {

    private static final Logger logger = LoggerFactory.getLogger(LoginFilter.class);

    /** * 配置过滤类型,有四种不同生命周期的过滤器类型 * 1. pre:路由之前 * 2. routing:路由之时 * 3. post:路由之后 * 4. error:发送错误调用 * @return */
    @Override
    public String filterType() {
        return "pre";
    }

    /** * 配置过滤的顺序 * @return */
    @Override
    public int filterOrder() {
        return 0;
    }

    /** * 配置是否需要过滤:true/需要,false/不需要 * @return */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /** * 过滤器的具体业务代码 * @return * @throws ZuulException */
    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String token = request.getParameter("token");
        if (token == null) {
            logger.warn("Token is empty");
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(401);
            try {
                context.getResponse().getWriter().write("Token is empty");
            } catch (IOException e) {
            }
        } else {
            logger.info("OK");
        }
        return null;
    }
}

测试过滤器

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务