分布式-Zuul 动态路由 &简单令牌桶

分布式-Zuul 动态路由

作用:隐藏服务,统一暴露接口,且可以让访问路径统一做验证,如只放行携带Token的路径
本文接上文(分布式-Ribbon 负载均衡

配置:
新建zuul-server项目,引入依赖

   		<!-- Zuul -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
          <!-- feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!-- eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
           <!-- actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

配置文件:

server.port=10010
spring.application.name=zuul-server
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
#健康检查(需要spring-boot-starter-actuator依赖)
eureka.client.healthcheck.enabled=true
# 续约更新时间间隔(默认30秒)
eureka.instance.lease-renewal-interval-in-seconds=10
# 续约到期时间(默认90秒)
eureka.instance.lease-expiration-duration-in-seconds=10

#zuul代理配置  zuul.routes.服务名.path,服务名要与注册的一致
#应用名映射
zuul.routes.service-a.path=/service-a/**
zuul.routes.service-a.service-id=service-a

过滤器:

/**
 * Zuul过滤器,实现了路由检查
 */
public class AccessFilter extends ZuulFilter {
    
    

    /**
     * 通过int值来定义过滤器的执行顺序
     */
    @Override
    public int filterOrder() {
    
    
        // PreDecoration之前运行
        return PRE_DECORATION_FILTER_ORDER - 1;
    }

    /**
     * 过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型:
     *     public static final String ERROR_TYPE = "error";
     *     public static final String POST_TYPE = "post";
     *     public static final String PRE_TYPE = "pre";
     *     public static final String ROUTE_TYPE = "route";
     */
    @Override
    public String filterType() {
    
    
        return PRE_TYPE;
    }

    /**
     * 过滤器的具体逻辑
     */
    @Override
    public Object run() {
    
    
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        System.out.println(String.format("%s AccessFilter request to %s", request.getMethod(),request.getRequestURL().toString()));
        String accessToken = request.getParameter("accessToken");
        //有权限令牌
        if (!StringUtils.isEmpty(accessToken)) {
    
    
            ctx.setSendZuulResponse(true);
            ctx.setResponseStatusCode(200);
            //可以设置一些值
            ctx.set("isSuccess", true);
            return null;
        } else {
    
    
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            ctx.setResponseBody("{\"result\":\"accessToken is not correct!\"}");
            //可以设置一些值
            ctx.set("isSuccess", false);
            return null;
        }
    }

    /**
     * 返回一个boolean类型来判断该过滤器是否要执行
     */
    @Override
    public boolean shouldFilter() {
    
    
        return true;
    }
}

启动类:

@EnableZuulProxy
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class ZuulServerApplication {
    
    

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

    @Bean
    public AccessFilter accessFilter() {
    
    
        return new AccessFilter();
    }

}

测试:
访问当前项目的路径http://127.0.0.1:10010/service-a/ribbon (service-a为配置的服务A的路径,这里不带accessToken)
在这里插入图片描述
访问当前项目的路径http://127.0.0.1:10010/service-a/ribbon?accessToken=12306在这里插入图片描述
顺带一提:
过滤器中可以使用令牌桶限流
这里提供一个简单的令牌桶:

/**
 * 简单的令牌桶限流
 */
public class RateLimiter {
    
    

    /**
     * 桶的大小
     */
    private Integer limit;

    /**
     * 桶当前的token
     */
    private static Integer tokens = 0;

    /**
     * 构造参数
     */
    public RateLimiter(Integer limit, Integer speed){
    
    
        //初始化桶的大小,且桶一开始是满的
        this.limit = limit;
        tokens = this.limit;

        //任务线程:每秒新增speed个令牌
        new Thread(() ->{
    
    
            while (true){
    
    
                try {
    
    
                    Thread.sleep(1000L);

                    int newTokens = tokens + speed;
                    if(newTokens > limit){
    
    
                        tokens = limit;
                    }else{
    
    
                        tokens = newTokens;
                    }
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * 根据令牌数判断是否允许执行,需要加锁
     */
    public synchronized boolean execute() {
    
    
        if (tokens > 0) {
    
    
            tokens = tokens - 1;
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
    
    
        //令牌桶限流:峰值每秒可以处理10个请求,正常每秒可以处理3个请求
        RateLimiter rateLimiter = new RateLimiter(10, 3);

        //模拟请求
        while (true){
    
    
            System.out.println("start");
            //在控制台输入一个值按回车,相对于发起一次请求
            Scanner scanner = new Scanner(System.in);
            scanner.next();

            //令牌桶返回true或者false
            if(rateLimiter.execute()){
    
    
                System.out.println("允许访问");
            }else{
    
    
                System.err.println("禁止访问");
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_42011565/article/details/110110178