其他分享
首页 > 其他分享> > SpringCloud Zuul学习

SpringCloud Zuul学习

作者:互联网

文章目录

SpringCloud Zuul学习

SpringCloud API网关Zuul

技术说明

SpringCloud Zuul

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之前

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

比如:

启动类开启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;
    }

}

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默认进行映射规则

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

路由规则通配符

通配符含义举例说明
匹配任意单个字符/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