前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[菜鸟SpringCloud实战入门]第九章:服务网关Zuul体验

[菜鸟SpringCloud实战入门]第九章:服务网关Zuul体验

作者头像
Rude3Knife的公众号
发布2019-08-06 15:34:29
5040
发布2019-08-06 15:34:29
举报
文章被收录于专栏:后端技术漫谈后端技术漫谈

前言

欢迎来到菜鸟SpringCloud实战入门系列(SpringCloudForNoob),该系列通过层层递进的实战视角,来一步步学习和理解SpringCloud。

本系列适合有一定Java以及SpringBoot基础的同学阅读。

每篇文章末尾都附有本文对应的Github源代码,方便同学调试。

Github仓库地址:

https://github.com/qqxx6661/springcloud_for_noob

菜鸟SpringCloud实战入门系列

你可以通过以下两种途径查看菜鸟SpringCloud实战入门系列

  • 关注我的公众号:Rude3Knife 点击公众号下方:技术推文——SpringCloud
  • 菜鸟SpringCloud实战入门专栏导航页(CSDN)

实战版本

  • SpringBoot:2.0.3.RELEASE
  • SpringCloud:Finchley.RELEASE

-----正文开始-----

[菜鸟SpringCloud实战入门]第九章:服务网关Zuul体验

服务网关 Zuul

Zuul介绍

外部的应用如何来访问内部各种各样的微服务呢?在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务端。

这个API网关便是Spring Cloud Zuul

Zuul提供动态路由,监控,弹性,安全等的边缘服务。Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器。

Zuul与Ribbon以及Eureka配合:

Zuul、Ribbon以及Eureka结合可以实现智能路由和负载均衡的功能;网关将所有服务的API接口统一聚合,统一对外暴露。外界调用API接口时,不需要知道微服务系统中各服务相互调用的复杂性,保护了内部微服务单元的API接口;网关可以做用户身份认证和权限认证,防止非法请求操作API接口;网关可以实现监控功能,实时日志输出,对请求进行记录;网关可以实现流量监控,在高流量的情况下,对服务降级;API接口从内部服务分离出来,方便做测试。

Zuul通过Servlet来实现,通过自定义的ZuulServlet来对请求进行控制。核心是一系列过滤器,可以在Http请求的发起和响应返回期间执行一系列过滤器。Zuul采取了动态读取、编译和运行这些过滤器。过滤器之间不能直接通信,而是通过RequestContext对象来共享数据,每个请求都会创建一个RequestContext对象。

Zuul生命周期如下图。

Zuul大部分功能都是通过过滤器来实现的,这些过滤器类型对应于请求的典型生命周期。

  • PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  • ERROR:在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

Zuul中默认实现的Filter:

图片来自:

http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html

在yml中可以禁用指定的Filter:

代码语言:javascript
复制
zuul:
    FormBodyWrapperFilter:
        pre:
            disable: true
新建Zuul子模块

本章需要用到之前章节的的模块:

  • eureka(eureka):第一章
  • eureka-hi(服务提供者):第二章

我们新建子模块(新建模块如有疑惑请看教程第一章),名为zuul

修改pom.xml:

代码语言:javascript
复制
<?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>
    <artifactId>zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>zuul</name>
    <packaging>jar</packaging>
    <description>Demo project for Spring Boot</description>

    <!--父工程的依赖-->
    <parent>
        <groupId>com.pricemonitor</groupId>
        <artifactId>springcloud</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>


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

</project>

在主类中加上@EnableZuulProxy:

代码语言:javascript
复制
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {

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

}

修改yml文件:

  • 设置端口为8773
  • 在eureka注册
  • 设置个producer路由,把/producer/映射到service-hi的服务
代码语言:javascript
复制
spring:
  application:
    name: gateway-service-zuul
server:
  port: 8773
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
zuul:
  routes:
    hello:
      path: /producer/**
      serviceId: service-hi
单机zuul运行

先后运行eureka,eureka-hi和zuul

使用官网来访问8763上eureka-hi提供的服务:

http://localhost:8773/producer/hi/rude3knife

得到:

说明网关正确转发了请求

zuul服务集群模拟

为了更好的模拟服务集群,我们再新开一个eureka-hi,端口号为8764,只需要修改yml的配置文件,然后重新运行即可。

你会看到来自8763和8764随机提供的服务:

zuul默认路由规则

Zuul其实已经代理所有注册到Eureka Server的微服务,并且Zuul的路由规则如下:http://ZUUL_HOST:ZUUL_PORT/微服务在Eureka上的serviceId/**会被转发到serviceId对应的微服务。

我们试验一下, http://localhost:8773/service-hi/hi/rude3knife :

zuul自定义filter

最上面的简介中我们提到,Zuul中有默认实现的Filter,当然也可以实现自己的自定义Filter,用来完成一些鉴权、流量转发、请求统计等工作。我们来实现一个自定义filter。

我们假设有这样一个场景,因为服务网关应对的是外部的所有请求,为了避免产生安全隐患,我们需要对请求做一定的限制,比如请求中含有Token便让请求继续往下走,如果请求不带Token就直接返回并给出提示。

新建TokenFilter类:

代码语言:javascript
复制
package com.pricemonitor.zuul;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;

public class TokenFilter extends ZuulFilter {

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

    @Override
    public String filterType() {
        // 可以在请求被路由之前调用
        return "pre";
    }

    @Override
    public int filterOrder() {
        // filter执行顺序,通过数字指定 ,优先级为0,数字越大,优先级越低
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        // 是否执行该过滤器,此处为true,说明需要过滤
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        logger.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString());

        String token = request.getParameter("token");

        if (StringUtils.isNotBlank(token)) {
            ctx.setSendZuulResponse(true); //对请求进行路由
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);
            return null;
        } else {
            ctx.setSendZuulResponse(false); //不对其进行路由
            ctx.setResponseStatusCode(400);
            ctx.setResponseBody("token is empty");
            ctx.set("isSuccess", false);
            return null;
        }
    }
}

随后,在启动类注入这个bean

代码语言:javascript
复制
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {

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

    @Bean
    public TokenFilter tokenFilter() {
        return new TokenFilter();
    }
}

运行eureka注册中心,eureka-hi服务提供者,zuul网关三个子模块。

不带token访问:

http://localhost:8773/producer/hi/rude3knife

带着token访问:

http://localhost:8773/producer/hi/rude3knife?token=dsa

通过上面这例子我们可以看出,我们可以使用“PRE”类型的Filter做很多的验证工作,在实际使用中我们可以结合shiro、oauth2.0等技术去做鉴权、验证。

网关路由熔断

之前我们学过hystrix的熔断,主要针对的是内部服务互相调用的熔断,而zuul的熔断则主要服务于外部接口调用。

注意:Zuul 目前只支持服务级别的熔断,不支持具体到某个URL进行熔断。

实现:主要是通过继承FallbackProvider(之前继承的是ZuulFallbackProvider:默认有两个方法,一个用来指明熔断拦截哪个服务,一个定制返回内容。)

网关路由重试

可以配合Spring Retry,来对暂时不可用的服务进行重试。

注意: 开启重试在某些情况下是有问题的,比如当压力过大,一个实例停止响应时,路由将流量转到另一个实例,很有可能导致最终所有的实例全被压垮。说到底,断路器的其中一个作用就是防止故障或者压力扩散。用了retry,断路器就只有在该服务的所有实例都无法运作的情况下才能起作用。这种时候,断路器的形式更像是提供一种友好的错误信息,或者假装服务正常运行的假象给使用者。 不用retry,仅使用负载均衡和熔断,就必须考虑到是否能够接受单个服务实例关闭和eureka刷新服务列表之间带来的短时间的熔断。如果可以接受,就无需使用retry。

本章代码

https://github.com/qqxx6661/springcloud_for_noob/tree/master/09-spring-cloud-zuul

参考

http://www.ityouknow.com/springcloud/2017/06/01/gateway-service-zuul.html

http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html

https://www.cnblogs.com/cralor/p/9234697.html

-----正文结束-----

菜鸟SpringCloud实战入门专栏全导航:通过以下两种途径查看

  • 关注我的公众号:Rude3Knife 点击公众号下方:技术推文——SpringCloud
  • 菜鸟SpringCloud实战专栏(CSDN)

关注我

我是蛮三刀把刀,后端开发。主要关注后端开发,数据安全,爬虫等方向。微信:yangzd1102

Github:@qqxx6661

个人博客:

  • CSDN:@qqxx6661
  • 知乎:@Zhendong
  • 简书:@蛮三刀把刀
  • 掘金:@蛮三刀把刀

原创博客主要内容

  • Java知识点复习全手册
  • Leetcode算法题解析
  • 剑指offer算法题解析
  • SpringCloud菜鸟入门实战系列
  • SpringBoot菜鸟入门实战系列
  • Python爬虫相关技术文章
  • 后端开发相关技术文章
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端技术漫谈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 菜鸟SpringCloud实战入门系列
  • 实战版本
  • -----正文开始-----
    • 服务网关 Zuul
      • 本章代码
        • 参考
        • -----正文结束-----
        • 关注我
          • 原创博客主要内容
          相关产品与服务
          负载均衡
          负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档