当前位置: 首页 > news >正文

SpringCloud 之 Zuul 基础使用与进阶

SpringCloud 之 Zuul 基础配置与进阶

  • 简介
  • 基础使用
    • 准备
    • 加依赖
    • 启动器加注释
    • 配置
    • 日志查看
    • 不加额外配置
    • 自定义服务访问以及服务忽略
    • 自定义路由名配置
    • 直接通过 URL 配置(有缺陷)
    • 直接通过 URL 配置(无缺陷)
    • 路由前缀
  • 进阶配置
    • 正则表达式指定Zuul的路由匹配规则
    • 自定义 Zuul 拦截器
    • 禁用自定义拦截器
    • 容错与回退

简介

Alt

基础使用

PS:zuul 基本需要配合 Eureka 使用,就不多介绍了:SpringCloud 之 Eureka 配置,Eureka 集群,Eureka 监听

准备

服务A
服务名:service-a
端口号:8080

服务B
服务名:service-b
端口号:8081

zuul 服务
服务名:zuul
端口号:8084

加依赖

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

启动器加注释

@EnableZuulProxy

配置

PS:Eureka 配置就不写了,和上面 Eureka 里面客户端配置一样

#配置端口号
server:
  port: 8084

spring:
  application:
    #配置服务名
    name: zuul

日志查看

# netflix ⽇志
logging:
  level:
    com.netflix: DEBUG

如无法掌握Zuul路由的规律,可将com.netflix包的日志级别设为DEBUG。这样,Zuul就会打印 转发的具体细节,从而帮助我们更好地理解Zuul的路由配置
**举个栗子**
意思:service-a返回服务器:172.0.0.1:8080 的请求是 /api/1

不加额外配置

在不加多余的配置文件配置时, zuul 就已经可以使用了,我们可以通过 IP + 服务名 的方式结合 Eureka 去访问,如图:
在这里插入图片描述 在这里插入图片描述

自定义服务访问以及服务忽略

虽然说不加配置就可以使用了,但是 Eureka 中所有的服务都能访问到这显然很不合理,也不安全
因此可以通过自定义服务与服务忽略结合,来提高可用性
ignored-services: '*' 表示忽略所有服务的前提下,只对配置的服务进行路由
ignored-services: service-a,service-b 当然也可以指定服务,逗号隔开
ignoredPatterns: /**/admin/** 如果想使用通配符进行忽略,那就要换 ignoredPatterns 才行
这个是忽略所有包含 /admin/ 的路径

# zuul 配置
zuul:
  # 使⽤'*'可忽略所有微服务
  # 通过此配置使得只有通过 routes 配置的路由才生效,更安全
  ignored-services: '*'
  routes:
    # 配置 service-a 服务的 访问路径为 /service-a/**
    service-a: /service-a/**

现在来访问下 service-a
在这里插入图片描述
再来访问下 service-b
在这里插入图片描述
可以看到,由于我们忽略了所有的服务后,只配置了service-a的配置,因此service-a能请求到,但是service-b就不行了

自定义路由名配置

这种配置效果和上面横写是一样的,访问路径同样是 http://127.0.0.1:8080/service-a/xxx

# zuul 配置
zuul:
  ignored-services: '*'
  routes:
    # 给路由⼀个名称,可以任意起名
    # 通过服务名配置路由
    service-a:
      service-id: service-a # 指定服务名
      path: /service-a/** # 服务名对应的路径

直接通过 URL 配置(有缺陷)

我们也可以直接通过 URL 进行配置,以 service-b 为例

# zuul 配置
zuul:
  ignored-services: '*'
  routes:
    # 通过 URL 配置路由
    service-b:
      url: http://127.0.0.1:8081 # 指定的url
      path: /service-b/** # url对应的路径

在这里插入图片描述
PS:使用这种方式配置的路由不会作为 HystrixCommand 执行,同时也不能使用 Ribbon 来负载均衡多个URL

直接通过 URL 配置(无缺陷)

# zuul 配置
zuul:
  ignored-services: '*'
  routes:
    # 改为通过服务名去匹配
    service-b:
      service-id: service-b # 指定的服务名
      path: /service-b/** # url对应的路径

# 禁⽤掉ribbon的eureka使⽤
ribbon:
  eureka:
    # 详⻅:http://cloud.spring.io/spring-cloud-static/Camden.SR4/#_example_disable_eureka_use_in_ribbon
    enabled: false

# 把 service-b 这个服务名与 IP 进行映射
service-b:
  ribbon:
    listOfServers: localhost:8081,localhost:8082

路由前缀

请求路径:http://127.0.0.1:8080/api/service-a/1
实际请求路径:http://127.0.0.1:8080/service-a/api/1

这种配置估计应用场景应该是不同版本的接口切换吧,切换版本的时候,前端直接在统一配置里把api换掉,所有请求接口就切到新的版本上

# Zuul 配置
zuul:
  ignored-services: '*'
  # 路由前缀,把 /api/service-a/1 映射到 /service-a/api/1
  prefix: /api
  strip-prefix: false
  routes:
  	service-a:
      service-id: service-a # 指定服务名
      path: /service-a/** # 服务名对应的路径

可以看到请求正常
在这里插入图片描述
再来看下日志输出
在这里插入图片描述
可以看到实际路径是http://127.0.0.1:8080/service-a/api/1

进阶配置

正则表达式指定Zuul的路由匹配规则

借助PatternServiceRouteMapper,实现从微服务到映射路由的正则配置
servicePattern 指定微服务的正则
routePattern 指定路由正则
正则怎么写,不会。。。

// zuul
@EnableZuulProxy
// Eureka 客户端
@EnableDiscoveryClient
// 由于没数据库,排除配置
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class, args);
    }

    // 正则表达式指定Zuul的路由匹配规则
    @Bean
    public PatternServiceRouteMapper serviceRouteMapper() {
        // 调⽤构造函数PatternServiceRouteMapper(String servicePattern, String routePattern)

        // servicePattern指定微服务的正则
        // routePattern指定路由正则
        // 最后一个 "-" 后面以 v 打头的为 version,之前的都是 name
        return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)", "${version}/${name}");
    }

}

自定义 Zuul 拦截器

zuul 除了帮助我们进行路由转发的功能外,还给我们提供了一个过滤器,我们可以通过继承 ZuulFilter 来实现 zuul 过滤器
我们可以通过这个过滤器来实现授权拦截,限流,日志记录等功能
filterType 指定过滤器的调用时机,方便我们在需要的时间段调用过滤器
filterOrder 指定过滤器执行的先后顺便,比如有两个 pre 的过滤器,就可以通过这个返回值控制两个过滤器的先后顺序
shouldFilter 指定过滤器是否使用
run 具体的过滤逻辑
这里我以一个简单的授权过滤器为例子

/**
 * Zuul 过滤器
 * 授权验证示例
 * @author: linjinp
 * @create: 2019-09-12 09:42
 **/
@Component
public class AuthFilter extends ZuulFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthFilter. class);

    /**
     * 是否开启验证
     * 正常项目里,这种属性应该放配置文件里
     */
    private static final Boolean AUTH = Boolean.TRUE;

    /**
     * 指定过滤器的调用时机
     * pre: 路由之前,如实现认证,记录调试信息等
     * routing: 路由时
     * post: 路由后,比如添加HTTP header
     * error: 发生错误时调用
     *
     * @return
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /**
     * 过滤器顺序
     * 比如有两个 pre 的过滤器,可以通过设置数字大小,控制两个过滤器执行先后
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * 判断是否启用该过滤
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤的具体逻辑
     *
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        // 启动验证
        if (AUTH) {
            String token = request.getHeader("Authorization");
            if (token == null) {
                LOGGER.info("该访问未进行授权");
                // 路由失败
                ctx.setSendZuulResponse(false);
                // 返回错误码
                ctx.setResponseStatusCode(401);
            } else {
                LOGGER.info("访问已授权");
                // 验证成功
                ctx.setSendZuulResponse(true);
                ctx.setResponseStatusCode(200);
            }
        } else {
            LOGGER.info("访问成功");
            // 没启用就直接成功
            ctx.setSendZuulResponse(true);
            ctx.setResponseStatusCode(200);
        }
        return null;
    }
}

看看日志打印
在这里插入图片描述

禁用自定义拦截器

除了在拦截器里直接关闭外,我们也可以通过在配置文件里配置关闭拦截器

zuul.<SimpleClassName>.<filterType>.disable=true

比如我要关闭上面的 AuthFilter 拦截器

zuul:
  # AuthFilter 拦截器开关
  AuthFilter:
    pre:
      disable: true

容错与回退

当我们使用 zuul 时,难免会出现接口因为各种原因请求不通的情况,比如服务宕机了之类的
为了保证系统的健壮性,我们可以通过实现 FallbackProvider 方法,来自定义返回内容

  • Edgware版本写法:实现 FallbackProvider
  • Edgware之前版本:实现 ZuulFallbackProvider
/**
 * 容错与回退
 * @author: linjinp
 * @create: 2019-09-12 11:27
 **/
@Component
public class CommonFallbackProvider implements FallbackProvider {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthFilter. class);

    /**
     * 需要回退的微服务,"*" 表示所有
     *
     * @return
     */
    @Override
    public String getRoute() {
        return "*";
    }

    /**
     * 回退逻辑
     * @param route
     * @param cause
     * @return
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 自定义返回值内容
     * @param status
     * @return
     */
    private ClientHttpResponse response(final HttpStatus status) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return status;
            }

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

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

            @Override
            public void close() {
            }

            @Override
            public InputStream getBody() throws IOException {
                LOGGER.info("服务不可⽤,请稍后再试");
                return new ByteArrayInputStream("服务不可⽤,请稍后再试。".getBytes());
            }

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

    }
}

现在我们把 service-a 停掉,然后请求试试,已模拟服务宕机
在这里插入图片描述

相关文章:

  • Navicat 连接 sqlserver 带端口号配置
  • SpringCloud 之 Config 配置中心与动态刷新
  • Java 基础:队列
  • Java 基础:栈
  • LeetCode 151. 翻转字符串里的单词
  • LeetCode 1. 两数之和
  • LeetCode 20. 有效的括号
  • LeetCode 202. 快乐数
  • LeetCode 217. 存在重复元素
  • LeetCode 200. 岛屿数量
  • LeetCode 349. 两个数组的交集
  • LeetCode 739. 每日温度
  • MySQL 集群(三):MySQL + Mycat 实现读写分离,主备切换集群
  • LeetCode 3. 无重复字符的最长子串
  • CentOS 之 pip 安装
  • @jsonView过滤属性
  • 10个最佳ES6特性 ES7与ES8的特性
  • Angular2开发踩坑系列-生产环境编译
  • Bytom交易说明(账户管理模式)
  • ESLint简单操作
  • java 多线程基础, 我觉得还是有必要看看的
  • Laravel5.4 Queues队列学习
  • Linux gpio口使用方法
  • PHP 小技巧
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Redash本地开发环境搭建
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • Vue组件定义
  • 聊聊springcloud的EurekaClientAutoConfiguration
  • 使用common-codec进行md5加密
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 小程序button引导用户授权
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • ​​​​​​​​​​​​​​Γ函数
  • #NOIP 2014#Day.2 T3 解方程
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (第27天)Oracle 数据泵转换分区表
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (算法)Game
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .NET Core Web APi类库如何内嵌运行?
  • .net framework 4.0中如何 输出 form 的name属性。
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • .net项目IIS、VS 附加进程调试
  • ::before和::after 常见的用法
  • @Autowired多个相同类型bean装配问题
  • [ vulhub漏洞复现篇 ] JBOSS AS 4.x以下反序列化远程代码执行漏洞CVE-2017-7504
  • []Telit UC864E 拨号上网