Spring-Cloud组件之API网关Zuul

什么是Zuul

Zuul是spring全家桶中的微服务API网关。
主要功能是路由和过滤两个主要的功能。
路由就是把外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础。
过滤呢就是对请求的处理过程进行干预(比如说认证资源,不符合的不让访问,还可以做性能监测等),是实现请求校验,服务聚合等功能的基础。

不得不说的spring全家桶是真的厉害,这个zuul和eureka整合后,也就是将zuul服务注册为eureka服务治理下的应用,zuul就可以或得其他微服务的信息,然后就可以通过zuul访问到其他微服务了。
在这里插入图片描述

路由

简单的说就是将不同的请求地址映射到不同的微服务上去,比如/api/user/*,那我们就讲这类请求映射到user微服务上,/api/power/*就映射到power微服务上去。

还是来写代码吧。新建一个module取名zuul9001。
pom.xml:

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>com.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>zuul9001</artifactId>

    <dependencies>
        <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>
    </dependencies>

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

</project>

application.yml

server:
  port: 9001
eureka:
  client:
    service-url:
      defaultZone: http://eureka3000.com:3000/eureka #Eureka服务端提供的注册地址

  instance:
    instance-id: server-zuul1 #此实例注册到Eureka服务端的唯一的实例ID
    prefer-ip-address: true #是否显示IP
    lease-renewal-interval-in-seconds: 10 #Eureka 客户端需要多长时间发送心跳给Eyreka服务器,表明他还活着 ,默认为30  (与下面配置的单位都是秒)
    lease-expiration-duration-in-seconds: 30 #Eureka 服务器在接收到实例的最后一次发出的心跳后,需要等待多久才可以将此实例删除,默认为90秒
spring:
  application:
    name: server-zuul #此实例注册到Eureka服务端的name

编写启动类Zuul9001Application

@SpringBootApplication
@EnableZuulProxy
public class Zuul9001Application {

    public static void main(String[] args) {
        SpringApplication.run(Zuul9001Application.class);
    }
}

好了,启动一下 eureka3000,power,zuul9001三个项目。
我们访问 http://localhost:9001/server-power/getPower.do 然后就可以顺利返回
在这里插入图片描述
这说明zuul已经成功路由了。
但是我们实际项目中,可不会访问/server-power。感觉不太好看啊,所以改一下配置文件:在最后面加上这个:

zuul:
  routes:
    power:
      serviceId: server-power  #eureika注册的名字
      path: /power/**   #转换访问路径

然后重启一下zuul9001。访问http://localhost:9001/server-power/getPower.do 和http://localhost:9001/power/getPower.do
是不是都可以正常放回了。
但是我们为了方位双通道访问,保证微服务的安全性,所以我们就应该只允许一个通道入口,既然如此,我们禁用一下/server-power/*访问吧。
改一下配置文件:

zuul:
  ignored-services: server-power  #禁用微服务名称直接访问  改为 "*" 则是禁用所有
  routes:
    power:
      serviceId: server-power  #eureika注册的名字
      path: /power/**   #转换访问路径

重启。在看看效果,http://localhost:9001/server-power/getPower.do是访问不进去的,只能通过http://localhost:9001/power/getPower.do访问了。这个意思看看注释就知道了。

我们可能会存在一个问题,就是路径冲突,什么意思呢,就是可能本项目和其他微服务有相同的访问路径,那如果我们这样访问,到底是访问微服务呢还是访问本项目的?所以我们给微服务和本项目区分一下,就是访问微服务的加一个路径。
改一下配置文件:

zuul:
  ignored-services: server-power  #禁用微服务名称直接访问  改为 "*" 则是禁用所有
  prefix: /api
#  strip-prefix: false  #防止路径被截断,比如我们power那边是 /api/getPower.do 写上这个就是防止 截断/api
  routes:
    power:
      serviceId: server-power  #eureika注册的名字
      path: /power/**   #转换访问路径

重启一下,是不是前面两个路径都不能访问了,这是因为我们给微服务做了一下区分,在路径前面必须加上/api才能访问了,现在应该访问http://localhost:9001/api/power/getPower.do

其实可能你们觉得这里我比较啰嗦了,我也只是想让大家理解一下每一个配置都是代表什么意思。
好吧,路由就差不多是这样,接下来我们再看看过滤吧。

过滤器

过滤器(filter)是zuul的核心组件 zuul大部分功能都是通过过滤器来实现的。
zuul中定义了4种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。
1.PRE:这种过滤器在请求被路由之前调用。可利用这种过滤器实现身份验证、在 集群中选择请求的微服务、记录调试信息等。

2.ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服 务的请求,并使用 Apache HttpCIient或 Netfilx Ribbon请求微服务。

3.POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准 的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

4.ERROR:在其他阶段发生错误时执行该过滤器。

用代码的方式看看。
新建一个LogFilter类,表示我们需要操作日志:


@Component
public class LogFilter extends ZuulFilter {
    /**
     * 拦截器类型
     * 1.PRE:这种过滤器在请求被路由之前调用。可利用这种过滤器实现身份验证、在 集群中选择请求的微服务、记录调试信息等。
     * 2.ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服 务的请求,并使用 Apache HttpCIient或 Netfilx Ribbon请求微服务
     * 3.POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准 的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
     * 4.ERROR:在其他阶段发生错误时执行该过滤器。
     * @return
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /**
     * 拦截器优先级,数字越小优先级越高。这里只是针对ZuulFilter的拦截器优先级,我们可能会定义几个ZuulFilter
     *
     * @return
     */
    @Override
    public int filterOrder() {
        //FilterConstants.PRE_DECORATION_FILTER_ORDER是ZuulFilter默认的拦截器。我们一般定义是在他之前或者在他之后,看实际情况来决定先后
        //+1表示在他之后
        return FilterConstants.PRE_DECORATION_FILTER_ORDER+1;
    }

    /**
     * 当前拦截器是否启动
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 实际的拦截需要执行的代码
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String s = ctx.get(FilterConstants.REQUEST_URI_KEY).toString();
        System.out.println(request.getRemoteAddr()+"访问了:"+request.getRequestURI()+"路由后的链接为:"+s);
        return null;
    }
}

这里看一下,我们实现了ZuulFilter接口。然后实现的几个接口分别代表什么就看一下注释,我们这里做的过滤比较简单,就是为了看一下 是谁访问的,然后访问的哪个接口,最后又被zuul路由到哪里了。
重启一下,然后看看控制台打印
在这里插入图片描述
这个ip有点问题,应该都知道的,本机ip需要转一下的,这里我就不演示了。
这个就证明了我这个过滤器生效了,其实这里只是简单的看一下,实际上这里可以做很多事情的,比如认证,性能监测,负载削减,压力测试,安全,静态响应处理等等事情。

容错与回退

zuul默认是整合了hystrix和ribbon的, 提供降级回退
新建一个FallBackProvider类实现FallbackProvider接口:

/**
 * zuul容错与回退
 */
@Component
public class FallBackProvider implements FallbackProvider {
    /**
     * 对哪个微服务做降级回退,"*"代表对所有微服务做降级回退
     * @return
     */
    @Override
    public String getRoute() {
        return "server-power";
    }

    /**
     *
     * @param route 出错了的微服务名称
     * @param cause 这个是出错的异常
     * @return
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {
        //判断异常属于什么异常
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    private ClientHttpResponse response(final HttpStatus status) {
        return new ClientHttpResponse() {

            /**
             * 这个是错误信息的枚举信息
             * @return
             * @throws IOException
             */
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return status;
            }

            /**
             * 这个就是状态码 比如 404 500 等
             * @return
             * @throws IOException
             */
            @Override
            public int getRawStatusCode() throws IOException {
                return status.value();
            }

            /**
             * 这个是错误信息的实际信息
             * @return
             * @throws IOException
             */
            @Override
            public String getStatusText() throws IOException {
                return status.getReasonPhrase();
            }


            /**
             * 在getBody响应完后调用
             */
            @Override
            public void close() {
            }

            /**
             * 返回具体的信息
             * @return
             * @throws IOException
             */
            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("系统异常".getBytes());
            }

            /**
             * 定义头部信息
             * @return
             */
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

这个也得仔细看看注释咯,解释都写在注释上面的,这里的代码是去官网拷贝的。这个就是微服务的容错也回退,我们可以这样,把power服务停掉,然后重启一下zuul9001,这样访问http://localhost:9001/api/power/getPower.do
然后就可以看到返回的是在这里插入图片描述
这是我们自定义的错误信息,返回这个比返回一堆看不懂的英文应该要好很多吧。

Zuul高可用-----集群

拷贝一个zuul9001取名zuul9002然后改一下端口为9002。这个就不贴代码了吧。

然后新建一个项目叫zuul
pom.xml

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>com.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>zuul</artifactId>
    <dependencies>
        <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>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

application.yml

server:
  port: 9000
eureka:
  client:
    service-url:
      defaultZone: http://eureka3000.com:3000/eureka #Eureka服务端提供的注册地址

  instance:
    instance-id: server-zuul0 #此实例注册到Eureka服务端的唯一的实例ID
    prefer-ip-address: true #是否显示IP
    lease-renewal-interval-in-seconds: 10 #Eureka 客户端需要多长时间发送心跳给Eyreka服务器,表明他还活着 ,默认为30  (与下面配置的单位都是秒)
    lease-expiration-duration-in-seconds: 30 #Eureka 服务器在接收到实例的最后一次发出的心跳后,需要等待多久才可以将此实例删除,默认为90秒
spring:
  application:
    name: zuul #此实例注册到Eureka服务端的name
zuul:
  ignored-services: server-power  #禁用微服务名称直接访问  改为 "*" 则是禁用所有
  prefix: /api
#  strip-prefix: false  #防止路径被截断,比如我们power那边是 /api/getPower.do 写上这个就是防止 截断/api
  routes:
    zuul:
      serviceId: server-zuul #eureika注册的名字
      path: /zuul/**   #转换访问路径

这里就是将zuul其他两个项目通过eureka路由一下。
编写启动类:

 @SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class);
    }
}

启动eureka3000,power,zuul9001,zuul9002,zuul五个项目,然后通过
http://localhost:9000/api/zuul/api/power/getPower.do 访问一下,在看看zuul9001和zuul9002的控制台,基本上是一边打印一次(这个不是轮询,我的意思是不会同时在两边打印)
在这里插入图片描述
这个就是zuul的高可用了啊,这里还是有一点小问题,如果这样部署集群,必须要给zuul也要部署集群,不然不行的。当然这个还可以通过nginx,feign来弄的,不过我就不演示了啊。有兴趣自己玩玩吧。

好了,到此为止了啊。马上进行下一篇。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值