Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

时间:2021-08-28 01:11:50

一. Zuul网关

1. 创建工程 并导入依赖

Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流
添加依赖

        <!-- zuul网关 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <!-- 计数器 -->
        <dependency>
            <groupId>com.marcosbarbero.cloud</groupId>
            <artifactId>spring-cloud-zuul-ratelimit</artifactId>
            <version>1.3.4.RELEASE</version>
        </dependency>

2. application.yml 配置文件

server:
  port: 7003
spring:
  application:
    name: zuul-server
eureka: # 注册中心
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/ #,http://localhost:7002/eureka/ #添加到的eureka注册中心的地址
  instance:
    prefer-ip-address: true #在eureka地址页面中显示微服务的ip地址和端口
    instance-id: ${spring.cloud.client.ip-address}:${server.port} #设置发送心跳时间间隔
    lease-renewal-interval-in-seconds: 5 #设置没有发送心跳 微服务在eureka中保存的最大时间
    lease-expiration-duration-in-seconds: 10
# zuul 网关
zuul:
  routes:
    user-server:  # 这里是路由id,随意写
      path: /**   # 这里是映射路径   user 是api/user/get   此处写api/**    实际的就为api/api/user/get
      serviceId: user-server #配置转发的微服务名称
    order-server:
      path: /**
      serviceId: order-server

3. 启动类添加注解

Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

4. 依次启动服务

Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

5. 进入浏览器访问测试

链接:http://localhost:7003/userserver/findUserOrder?uid=2
展示此数据就说明成功了
Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

二. ZuulFilter 过滤器

1. ZuulFilter简介

以通过配置 urlPatterns 来拦截对应的请求。而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对应的使用场景。

  1. PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  2. ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfifilx Ribbon请求微服务。
  3. POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP
    Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  4. ERROR:在其他阶段发生错误时执行该过滤器。

2. 在filters包下创建PowerFilter类

Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

package com.ddz.filters;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class PowerFilter extends ZuulFilter {

    /* 过滤器类型,可选值有 pre、route、post、error */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /* 过滤器的执行顺序,数值越小,优先级越高 */
    @Override
    public int filterOrder() {
        return 0;
    }

    /* 是否执行该过滤器,true 为执行,false 为不执行,这个也可以利用配置中心来实现,达到动态的开启和关闭过滤器 */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /* 业务逻辑,本次是判断请求的url是否带有token,没有则通过设置 ctx.setSendZuulResponse(false),
    告诉 Zuul 不需要将当前请求转发到后端的服务了,通过setResponseBody 返回数据给客户端 */
    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();        //获取上下文
        HttpServletRequest request = context.getRequest();
        String token = request.getParameter("token");                           //获取token
        if (token == null || "".equals(token)) {
            context.setSendZuulResponse(false);                             // 过滤该请求
            context.setResponseStatusCode(401);                             // 返回错误码
            context.setResponseBody("{'msg':'401 account without'}");       // 返回错误信息
            return "access";
        }
        return "next";
    }
}

3. 再次访问网址

链接:http://localhost:7003/userserver/findUserOrder?uid=2
不带token访问
Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流
带token 访问
Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

三. 计数器限流

1. 在application.yml 配置文件添加!

zuul:
  ratelimit:
    enabled: true #开启限流
    policies: # 多个微服务
      user-server: # 上面写的路由id
        limit: 3 #60s 内请求超过 3 次,服务端就抛出异常,60s 后可以恢复正常请求
        quota: 30 #单位时间内允许访问的总时间(单位时间窗口期内,所有的请求的总时间不能超过这个时间限制)
        refresh-interval: 10 #单位时间设置
        type: origin #针对 IP 进行限流,不影响其他 IP

2. 再去连续快速的访问网址

链接:http://localhost:7003/userserver/findUserOrder?uid=2&token=1
Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流
以上错误信息,返回给前端易读性差下面对错误信息优化

3.再 controller 包下创建 ErrorController 错误信息控制器

Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

package com.ddz.controller;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class ErrorHander implements ErrorController {
    @Override
    public String getErrorPath() {
        return "error";
    }

    /* 自定义的异常信息方法 */
    @RequestMapping(value = "error")
    public Map<String, Object> error() {
        Map<String, Object> dto = new HashMap<>();
        dto.put("msg", "计数器访问限流");              //提示信息
        dto.put("code", 401);                        // 错误码
        return dto;
    }
}

4. 再去连续快速的访问网址

链接:http://localhost:7003/userserver/findUserOrder?uid=2&token=1
Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

四. 令牌桶限流

1. 在 filters 包下创建 LimitFilter 类

Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

package com.ddz.filters;

import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class LimitFilter extends ZuulFilter {
    // 定义一个令牌桶,每秒产生2个令牌,即每秒最多处理2个请求
    private static final RateLimiter RATE_LIMITER = RateLimiter.create(2);

    /* 过滤器类型,可选值有 pre、route、post、error */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    /* 过滤器的执行顺序,数值越小,优先级越高 */
    @Override
    public int filterOrder() {
        return -5;
    }

    /* 是否执行该过滤器,true 为执行,false 为不执行,这个也可以利用配置中心来实现,达到动态的开启和关闭过滤器 */
    @Override
    public boolean shouldFilter() {
        RequestContext context = RequestContext.getCurrentContext();
        //HttpServletRequest request = context.getRequest();
        // tryAcquire() 只要能够马上获致到1个令牌,则返回true,不阻塞
        // tryAcquire(5, 3, TimeUnit.SECONDS) 在3秒钟内可以获取到5个令牌,则返回true,不阻塞
        // acquire(5) 获取到5个令牌,否则一直等待,会阻塞,返回值为阻塞的时长
        // acquire() 获取到1个令牌,否则一直等待,会阻塞,返回值为阻塞的时长
        if (!RATE_LIMITER.tryAcquire()) {
            context.getResponse().setContentType("application/json; charset=utf-8");
            context.setSendZuulResponse(false);                            // false指当前请被过滤调
            context.setResponseStatusCode(401);                            // 错误码
            context.setResponseBody("{code:401,msg:'你限流了'}");           // 错误信息
            return false;
        }
        return true;
    }

    /* 业务逻辑 */
    @Override
    public Object run() throws ZuulException {
        return null;
    }
}

2. 在 application.yml 中注释掉计数器限流配置

Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

3. 去浏览器连续快速访问

链接:http://localhost:7003/userserver/findUserOrder?uid=2&token=1
Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流