Zuul 提供的三大功能:代理 + 路由 + 过滤

Zuul 包含了对请求的 “ 路由 ” 和 “ 过滤 ” 两个最主要的功能:其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础 。

在 Spring Cloud 中 Zuul 和 Eureka 进行整合,将 Zuul 自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获得其它微服务列表信息,也即以后的访问微服务都是通过 Zuul 来跳转后获得,特别注意的是,Zuul 服务最终也需要注册进 Eureka的。

在SpringCloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服。,服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理,配置服务的配置文件放在git仓库,方便开发人员随时改配置。

ribbon是对服务之间调用做负载,是服务之间的负载均衡,zuul是可以对外部请求做负载均衡

准备工作

依次启动之前的五个程序,接着创建service-zuul项目。

配置文件

Pom文件配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>zuul</name>
    <description>Demo project for Spring Boot</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.RC1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>


</project>

配置文件有的版本可能有兼容问题,之前也提过了,2.1.x和2.0.x,我还是以之前的版本做的。

添加注解

@EnableZuulProxy
@EnableDiscoveryClient

在项目的启动类上添加以上两个注解。
配置application.yml文件

eureka:
  client:
    serviceurl:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 8775
spring:
  application:
    name: service-zuul
zuul:
  routes:
    api-a:
      path: /api-a/**
      serviceId: service-r
    api-b:
        path: /api-b/**
        serviceId: service-feign
ribbon:
  ReadTimeout: 60000
  ConnectTimeout: 60000

我这里踩了两个坑,第一个是ribbon:的这个,不加超时的话会显示报错。虽然Idea会识别不了,但实际还是生效了。
另外是api-a和api-b,一定要对齐!!!! 格式很重要,之前我api-b多了两个空格,程序没报错,但是运行之后进入api-b会出错。

访问
http://localhost:8775/api-a/hi?name=ccdbbhttp://localhost:8775/api-b/hi?name=ccdbb 显示:

hi ccdbb,i am from port:8770
hi ccdbb,i am from port:8771

过滤

过滤在我参考的那个博主的文章里面其实写的不是很详细,这里我对代码的内容进行了具体的一些说明。
创建Filter.class。 继承ZuulFilter 里面要写filterType()等方法,没写之前会红字,没关系。
具体的代码如下:

package com.example.zuul;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
@Component
public class Filter extends ZuulFilter {
    private static Logger log = LoggerFactory.getLogger(Filter.class);
    /**
     * 过滤器的类型,决定了过滤器会在请求的那个生命周期中执行,
     *一共有四种类型
     * pre:可以在请求被路由之前调用
     * route:在路由请求时候被调用
     * post:在route和error过滤器之后被调用
     * error:处理请求时发生错误时被调用
     * @return
     */

    @Override
    public  String filterType(){
        return "pre";
    }
    /**
     * 过滤器的执行顺序,当请求在一个阶段中存在多个过滤器的时候
     * 需要根据该方法返回的值来一次执行。
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }
    /**
     * 判断该过滤器是否需要执行,如果返回true,
     * 那么该过滤器对所有的请求 都会生效
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }
    /**
     * 过滤器的具体逻辑,可以通过 requestContext.setSendZuulResponse(false);来过滤请求,
     * 不对其进行路由。
     * @return
     * @throwsZuulException
     */

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
        Object accessToken = request.getParameter("token");
        if(accessToken == null) {
            log.warn("token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("token is empty");
            }catch (Exception e){}

            return null;
        }
        log.info("ok");
        return null;
    }
}

这个时候访问 http://localhost:8775/api-a/hi?name=happy 显示

token is empty

加上token后:http://localhost:8775/api-a/hi?name=happy&token=1

hi happy,i am from port:8770