网关的概念

在这里插入图片描述
API Gateway 网关,是介于客户端、服务器端之间的中间层,是系统对外的唯一入口,可以统一过滤请求,提供路由转发、权限验证、监控、缓存、限流等功能。

网关也要注册到注册中心上,从注册中心获取服务节点列表,使用内置的Ribbon进行负载均衡,确定路由到服务集群的哪个节点。

 

网关的功能

1、统一接入

  • 智能路由
  • AB测试、灰度测试
  • 负载均衡、容灾处理
  • 日志埋点

2、流量监控

  • 限流
  • 服务降级

3、安全防护

  • 鉴权
  • 监控
  • 内外网隔离

 

主流的网关

  • zuul:Netflix的开源组件,性能一般
  • gateway: springcloud官方提供的网关,替代zuul,性能高。
  • nginx+lua:在nginx上执行lua脚本,对请求进行处理,性能高。这个事代理层的网关,上面2个是应用层的网关。

 

搭建zuul网关、实现路由转发

1、新建子模块api-gateway,创建时只需勾选Spring Cloud Discovery -> Eureka Discovery Client ,Spring Cloud Routing -> Routing[Maintenance]
也可以手动添加依赖

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

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

Zuul的依赖中已经包含了Hystrix的依赖,可以使用hystrix在网关处进行限流

 

2、引导类上加

  • @EnableEurekaClient。可根据需求设置负载均衡策略。
  • @EnableZuulServer或@EnableZuulProxy。@EnableZuulProxy中已经包含了Hystrix的注解@EnableCircuitBreaker。

 

3、application.yml

server:
  port: 9000

spring:
  application:
    name: api-gateway
  servlet:
    multipart:
      #请求的最大尺寸,默认10M
      max-request-size: 1024MB
      #上传所允许的单个文件的最大尺寸,默认1M
      max-file-size: 1024MB


eureka:
  client:
    serviceUrl:
      defaultZone: http://127.0.0.1:8761/eureka/


zuul:
  #在服务名前加前缀,这样可以用/api-gateway/拦截所有请求
  prefix: /api-gateway/
  #zuul默认会过滤请求头,剔除请求头中的sensitive-headers部分(默认为Cookie、Set-Cookie、Authorization)后,再将请求转发给服务,在服务中是取不到cookie的
  #取消对请求头的默认过滤,不设置值即为null
  sensitive-headers:

  ##默认按服务名进行路由,可以取消原来的映射规则,自定义路由规则,但不推荐这样做
#  ignored-patterns: /*-service/**
#  routes:
#    user-service: /api-gateway/user-service/**
#    order-service: /api-gateway/order-service/**

使用网关之后,所有请求都要应该通过网关来访问,比如通过网关来访问user-service:127.0.0.1:9000/api-gateway/user-service/api/v1/user/…

网关集群后,使用nginx做负载均衡,所有请求都应该发给nginx。

通过网关访问服务也是服务调用,zuul本身已经集成了hystrix,同样可以设置容错保护,设置相关配置

zuul:
  host:
    connect-timeout-millis: 100000
    socket-timeout-millis: 100000

ribbon:
  ReadTimeout: 100000
  ConnectTimeout: 100000

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 100000

 

使用zuul过滤请求

在这里插入图片描述
新建包filter用来存放过滤器

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component //放到spring容器中
public class ValidateFilter extends ZuulFilter {  //继承抽象类ZuulFilter

    /*
    指定过滤类型(时机),"pre"是路由转发之前,"routing"是路由转发之时,"post"是返回响应给浏览器之前,"error"是发生错误时
    前处理用pre,后处理用post
     */
    @Override
    public String filterType() {
        return "pre";
    }


    /*
    要执行多个拦截器,需要指定这些拦截器调用的先后顺序,数值越小,优先级越高、越先执行
    可以在FilterConstants类中查看Zuul自带的拦截器的Order,自定义拦截器的Order要参考它们进行设置,不能想设置多大就设置多大
    前处理一般设置为0~9
     */
    @Override
    public int filterOrder() {
        return 0;
    }


    /*
    是否要使用此过滤器,默认false,改为true
    如果要关闭此拦截器,改为false即可
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }


    /*
    核心方法,用来过滤请求
     */
    @Override
    public Object run() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        //前处理,获取request
        HttpServletRequest request = currentContext.getRequest();
        //获取参数
        String token = request.getParameter("token");
        //token要和数据库查到的进行比较,此处随便写一个代替
        if (token==null || !token.equals("123456")) {
            //如果token错误,直接返回响应,不转发给服务。只是不转发给服务,此方法后面部分的代码还是会执行
            currentContext.setSendZuulResponse(false);
            currentContext.setResponseStatusCode(400);
            //输出信息到浏览器
            try {
                currentContext.getResponse().getWriter().write("token is invalid");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 有时候可能要对指定的url进行拦截处理
        String requestURI = request.getRequestURI();
        if (requestURI.contains("/api-gateway/user-service/......")){
            //......
        }
        if (requestURI.equals("/api-gateway/user-service/.....")){
            //......
        }

        return null;
    }

}

不建议使用大量的过滤器,因为会加大时间开销,拉低性能。

网关中写通用的过滤,比如校验用户是否登录;如果是对部分服务进行过滤,尽量在shouldFilter()中就判断请求地址;如果只是个别服务的过滤,写在服务自身中。

 

使用zuul进行限流

系统性能有限,过多请求会冲垮系统,所以一般都要做限流。一般用hystrix做服务层限流,zuul可以用谷歌的guava框架实现网关层限流,nginx上也可以限流。

zuul集成了hystrix,可以在过滤器中使用hystrix限流,在回退方法中写代替方案。zuul也集成了guava,可以在过滤器中用guava限流,直接用即可。guava用得更多。
 

在过滤器中使用guava限流

import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;


@Component
public class LimitFilter extends ZuulFilter {
    //RateLimiter是com.google.common.util.concurrent.RateLimiter,不要导错了
    //指定最大访问量,数值一般是压测得到的qps上限,此处设置为1000
    private static final RateLimiter RATE_LIMITER = RateLimiter.create(1000);

    @Override
    public String filterType() {
        return "pre";
    }


    @Override
    public int filterOrder() {
        //限流的过滤器放在自定义前处理的最前面
        return -4;
    }


    @Override
    public boolean shouldFilter() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String requestURI = request.getRequestURI();
        //只限流指定服务,此处限流的服务是user-service,通过zuul请求user-service的qps不能超过1000
     //各个服务能承受的最大qps不同,一般要分开设置。如果多个服务能承载的最大qps差不多,可以一起设置
        if (requestURI.contains("/api-gateway/user-service/")){
            return true;
        }
        return false;
    }


    @Override
    public Object run() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        //如果超过设置的上限,直接返回响应,不转发给服务
        if (!RATE_LIMITER.tryAcquire()){
            currentContext.setSendZuulResponse(false);
            //返回给浏览器的状态码是429 过多请求
            currentContext.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
        }
        return null;
    }
    
}
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐