ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SpringCloud Zuul学习

2022-03-21 12:03:16  阅读:198  来源: 互联网

标签:feign Zuul service SpringCloud 学习 springcloud zuul 路由


文章目录

SpringCloud Zuul学习

SpringCloud API网关Zuul

技术说明

  • 在SpringCloud2020.0.1发布之后,由于Netflix的技术在2019年就开始停更维护,于是SpirngCloud决定对于Netflix的技术全部被抛弃,其中包括zuul组件,官方推荐使用Spring Gateway代替Zuul,并且国内大部分公司也开始替换API网关技术,综上,Zuul开始过时,可以将更多的精力转向SpringGateway上,Zuul只用来学习,了解

  • 由于版本迭代更新和弃用,学习成本高,如果要完全实现Zuul,则需要将SpringCloud版本降至2020.0之前,SpringBoot版本降至2.2~2.3

SpringCloud Zuul

  • Spring Cloud 这个一站式的微服务开发框架基于 Netflix Zuul 实现了 Spring Cloud Zuul,采用 Spring Cloud Zuul 即可实现一套 API 网关服务
  • zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用

SpringCloud微服务架构图

上图中,有feign声明式服务消费,SpringCloud LoadBalance实现负载均衡,Hystrix实现服务熔断

zuul的API网关服务在该图中可以提供:

  • 管理对外,对内接口调用,即请求路由
  • 权限管理

搭建SpringCloud Zuul服务

导入依赖

<!--导入Eureka客户端依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<!--springcloud-zuul依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>

依赖说明

由于2020年后Netflix组件被SpringCloud抛弃,如果要真正实现Zuul,SpringCloud版本有要求在2020.0.1之前

  • SpringCloud版本

    2020.0.1之前

  • SpringBoot版本

    由于需要对应SpringCloud版本,所以需要在2.2~2.3之间

如果不如此选择,许多功能会受限制

比如:

  • 如果SpringBoot版本在2.5.0及以上,Zuul服务调用时,会造成HTTP Status 500 – Internal Server Error异常错误
  • 如果SpringCloud版本在2020.0.1之上(对应SpringBoot版本在2.3之上),会造成500转发异常,无法在注册中心发现其他微服务,但实际已经注册在注册中心了
  • 不能使用微服务名称来转发,但是能使用url来转发(SpringCloud 2020.0.4,SpringBoot 2.4.8)

启动类开启ZuulAPI网关

// 开启Zuul的API网关服务
@EnableZuulProxy
//@EnableEurekaClient
@SpringBootApplication
public class SpringCloudZuulApplication {

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

}

其中@EnableEurekaClient可以不使用,也会注册在eureka注册中心

Zuul配置类

server:
  port: 9100

spring:
  application:
    # 微服务名称
    name: springcloud-zuul

eureka:
  client:
    service-url:
      # 指定Eureka注册中心地址,用于将微服务注册进Eureka服务端
      defaultZone: http://eureka-cluster-a:8201/eureka/, http://eureka-cluster-b:8202/eureka/, http://eureka-cluster-c:8203/eureka/
  instance:
    # 将IP注册到Eureka Server上,而如果不配置ipAddress就是机器的主机名hostname
    prefer-ip-address: true
    # 使得服务实例在eureka界面增加显示版本号,格式:地址:微服务名:端口号:版本号
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
    # 每间隔 2s,向服务端发送一次心跳,证明自己依然"存活"
    lease-renewal-interval-in-seconds: 2
    # 告诉服务端,如果我 10s 之内没有给你发心跳,就代表我故障了,将我踢出掉
    lease-expiration-duration-in-seconds: 10

# zuul配置路由规则
zuul:
  prefix: /api
  routes:
    # 自定义的名字,只要保证子属性path和serviceId对应即可
    route-feign:
      # 访问API网关的时候,输入地址栏的路径uri
      path: /route-feign/**
#      # 路由转发 对应的微服务名称
#      serviceId: springcloud-feign-consumer

      # 映射路径对应的url,通过url来路由,耦合度较高
      url: http://localhost:9000/

#    # 简化方式,服务id和路由名称一致
#    springcloud-feign-consumer: /route-feign/**

  # 忽略相关接口
  ignored-patterns: /**/hello/**

此处配置完后,通过zuul服务,即可访问其他的微服务了

Zuul请求过滤

Spring cloud Zuul 就像一个安检站,所有请求都会经过这个安检站, 所以我们可以在该安检站内实现对请求的过滤

/**
 * @author slipperySoap
 * @version 1.0
 * ClassName: AuthFilter
 * Description:
 * Date: 2021/12/20 13:16
 */
@Component
public class AuthFilter extends ZuulFilter {

    @Resource
    ObjectMapper objectMapper;

    /**
     * 过滤器的类型
     * @return pre代表在路由之前执行过滤器
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 过滤器的执行顺序,从小到达排序
     * @return 当前排序下下标为0
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 过滤器是否执行
     * @return true代表当前过滤器打开
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 具体过滤逻辑
     *
     * @return 返回值暂时没有任何意义,可以返回为null
     * @throws ZuulException
     */
    @SneakyThrows
    @Override
    public Object run() throws ZuulException {
        // 获取请求体
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        // 简单的从请求参数中拿token令牌
        String token = request.getParameter("token");

        // 简单的过滤逻辑,如果没有令牌就不放行
        if (token == null) {
            // 设置setSendZuulResponse为false代表非法请求不放行
            ctx.setSendZuulResponse(false);
            // 设置响应码
            ctx.setResponseStatusCode(ResultCode.UNAUTHENTICATED.code());
            // 设置请求头中 媒体类型 信息
            ctx.addZuulResponseHeader("content-type","application/json;charset=utf-8");
            // 设置请求体
            Result result = new Result(ResultCode.UNAUTHENTICATED);
            String resultJson = objectMapper.writeValueAsString(result);
            ctx.setResponseBody(resultJson);
        }
        return null;
    }

}
  • filterType()

    方法的返回值为过滤器的类型,过滤器的类型决定了过滤器在哪个生命周期执行

    • pre 表示在路由之前执行过滤器
    • route 表示在路由之时执行过滤器
    • post 表示在路由之后执行过滤器
    • error表示路由在发生错误后的过滤器
  • filterOrder()

    表示过滤器的执行顺序,当过滤器很多时,我们可以通过 该方法的返回值来指定过滤器的执行顺序

  • shouldFilter()

    用来判断过滤器是否执行,true 表示执行,false 表示不 执行

  • run()

    表示过滤的具体逻辑

Zuul路由规则

服务名间接/直接指定路由

#配置路由规则
#属性api-wkcto为自定的值,而/api-wkcto/**为指定的请求url
zuul.routes.api-wkcto.path=/api-wkcto/**
zuul.routes.api-wkcto.serviceId=05-springcloud-service-feign

当访问地址符合/api-wkcto/**规则的时候,会被自动定位到

05-springcloud-service-feign

服务上,不过两行代码有点麻烦,还可以简化为

zuul.routes.05-springcloud-service-feign=/api-wkcto/**

zuul.routes 后面跟着的是服务名,服务名后面跟着的是路径规则,这种配置方 式更简单

默认路由

如果映射规则配置我们什么都不写,系统也给我们提供了一套默认的配置规则

等价于这样配置:

#默认的规则等价于
zuul.routes.05-springcloud-service-feign.path=/05-springcloud-service-feign/**
zuul.routes.05-springcloud-service-feign.serviceId=05-springcloud-service-feign

忽略服务的默认路由

不配置映射规则,Eureka上的所有服务会被Zuul默认进行映射规则

而对于部分微服务,我们不想提供对外的服务,可以进行忽略默认路由配置

  • 忽略服务

    #忽略掉服务提供者的默认规则
    zuul.ignored-services=01-springcloud-service-provider
    
  • 忽略指定请求

    #忽略掉某一些接口路径
    zuul.ignored-patterns=/**/hello/**
    

路由规则通配符

通配符含义举例说明
匹配任意单个字符/05-springcloud-service-feign/?匹配 /05-springcloud-service-feign/a, /05-springcloud-service-feign/b, /05-springcloud-service-feign/c 等
*匹配任意数量的字符/05-springcloud-service-feign/*匹配 /05-springcloud-service-feign/aaa, /05-springcloud-service-feign/bbb, /05-springcloud-service-feign/ccc 等, 无法匹配 /05-springcloud-service-feign/a/b/c
**匹配任意数量的字符/05-springcloud-service-feign/**匹配 /05-springcloud-service-feign/aaa, /05-springcloud-service-feign/bbb, /05-springcloud-service-feign/ccc 等, 也可以匹配 /05-springcloud-service-feign/a/b/c

自定义路由业务

一般情况下 API 网关只是作为各个微服务的统一入口,但是有时候我们可能 也需要在 API 网关服务上做一些特殊的业务逻辑处理,那么我们可以让请求到 达 API 网关后,再转发给自己本身,由 API 网关自己来处理,那么我们可以进 行如下的操作

在 06-springcloud-api-gateway 项目中新建如下 Controller:

@RestController
public class GateWayController {
    @RequestMapping("/api/local")
    // 一些路由业务处理
    public String hello() {
        return "exec the api gateway.";
    }
}

然后在 application.properties 文件中配置:

# 指定路由
zuul.routes.gateway.path=/gateway/**
# 使用url指定转发的路径
zuul.routes.gateway.url=forward:/api/local

Zuul异常处理

Spring Cloud Zuul 对异常的处理是非常方便的,但是由于 Spring Cloud 处于 迅速发展中,各个版本之间有所差异,本案例是以 Finchley.RELEASE 版本为例, 来说明 Spring Cloud Zuul 中的异常处理问题。

首先我们来看一张官方给出的 Zuul 请求的生命周期图:

Zuul生命周期

  1. 正常情况下所有的请求都是按照 pre、route、post 的顺序来执行,然后由 post 返回 response
  2. 在 pre 阶段,如果有自定义的过滤器则执行自定义的过滤器
  3. pre、routing、post 的任意一个阶段如果抛异常了,则执行 error 过滤器

我们可以有两种方式统一处理异常:

1、禁用 zuul 默认的异常处理 SendErrorFilter 过滤器,然后自定义我们自己的

Errorfilter 过滤器

zuul.SendErrorFilter.error.disable=true
@Component
public class ErrorFilter extends ZuulFilter {
    private static final Logger logger =
        LoggerFactory.getLogger(ErrorFilter.class);
    @Override
    public String filterType() {
        return "error";
    }
    @Override
    public int filterOrder() {
        return 1;
    }
    @Override
    public boolean shouldFilter() {
        return true;
    }
    @Override
    public Object run() throws ZuulException {
        try {
            RequestContext context = RequestContext.getCurrentContext();
            ZuulException exception = (ZuulException)context.getThrowable();
            logger.error("进入系统异常拦截", exception);
            HttpServletResponse response = context.getResponse();
            response.setContentType("application/json; charset=utf8");
            response.setStatus(exception.nStatusCode);
            PrintWriter writer = null;
            try {
                writer = response.getWriter();
                writer.print("{code:"+ exception.nStatusCode +",message:\""+
                             exception.getMessage() +"\"}");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(writer!=null){
                    writer.close();
                }
            }
        } catch (Exception var5) {
            ReflectionUtils.rethrowRuntimeException(var5);
        }
        return null;
    }
}

2、自定义全局 error 错误页面

@RestController
public class ErrorHandlerController implements ErrorController {
    /**
 * 出异常后进入该方法,交由下面的方法处理
 */
    @Override
    public String getErrorPath() {
        return "/error";
    }
    @RequestMapping("/error")
    public Object error(){
        RequestContext ctx = RequestContext.getCurrentContext();
        ZuulException exception = (ZuulException)ctx.getThrowable();
        return exception.nStatusCode + "--" + exception.getMessage();
    }
}

标签:feign,Zuul,service,SpringCloud,学习,springcloud,zuul,路由
来源: https://blog.csdn.net/slipperySoap/article/details/123573633

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有