Zuul网关

服务网关视为服务架构中一个不可或缺的部分,通过服务网关统一向外系统提供Rest API的过程,除了具备服务路由、负载均衡功能外,它还具备权限控制等功能。Spring Cloud Netflix中的Zuul就担任了遮掩的一个角色,为微服务架构提供了前门保护的作用,同时将权限控制这些较重的非业务逻辑内容迁移到服务路由层面,使得服务集群主体能够具备更高的可复用性和可测试性。


简介

Zuul是Netflix开源的为服务网关,他可以和Eureka、Ribbon、Hystrix等组件配合使用。Zuul的核心是一系列的过滤器,这些过滤器可以完成以下功能。

  • 身份认证与安全:识别每个资源的验证要求,并拒绝那些与要求不符的请求;
  • 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产视图;
  • 动态路由:动态地将请求路由到不同的后端集群;
  • 压力测试:逐渐增加指向集群的流量,以了解性能;
  • 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求;
  • 静态响应处理:在边缘位置直接建立部分响应,从而避免其转发到内部集群;
  • 多区域弹性:跨越AWS Region进行请求路由,旨在实现ELB使用的多样化,以及让系统的边缘更贴近系统使用者。

之前的架构

在这里插入图片描述

Zuul加入后的架构

在这里插入图片描述

不管是来自于客户端的请求,好事服务内部调用,一切对服务的请求都会经过zuul这个网关,然后再由网关来实现鉴权、动态路由等操作,Zuul就是我们服务统一入口。

快速入门

添加依赖

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

编写启动类

@SpringBootApplication
@EnableZuulProxy   //开启网关功能
public class ZuulDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulDemoApplication.class);
    }
}

编写配置

server:
  port: 10010  #服务端口
spring:
  application:
    name: api-gateway #指定服务名

编写路由规则

我们需要Zuul来代理user-service服务,获取被代理服务的访问路径

在这里插入图片描述

#映射规则
zuul:
  routes:
    user-server: #这里是路由的id,只保证唯一性即可,不要求格式
      path: /user-server/**  #这里是映射的路径
      url: http://127.0.0.1:8081 #这里是映射路径的实际url地址

我们将符合path规则的一切请求都代理到了指定的真实url。

启动测试

访问路径中需要加上配置规则的映射路径;

在这里插入图片描述

面向服务的路由

在刚才的路由规则中,我们把路径对应的服务地址写死了,如果同一服务有多个实例的话,这样做显然会不合理的。我们应该根据服务名称,去Eureka注册中心中查找服务对应的所有实例列表,然后进行动态路由才对。

添加Eureka客户端依赖

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

开启Eureka客户端发现功能

@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class ZuulDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulDemoApplication.class);
    }
}

添加Eureka配置,获取服务信息

eureka:
  client:
    registry-fetch-interval-seconds: 5 #获取服务列表的周期
    service-url:
      defaultZone: http://127.0.0.1:10087/eureka
  instance:
    prefer-ip-address: true
    ip-address: 127.0.0.1

修改映射配置,通过服务名称获取

#映射规则
zuul:
  routes:
    user-server: #这里是路由的id,只保证唯一性即可,不要求格式
      path: /user-server/**  #这里是映射的路径
      url: user-server  

在这里插入图片描述
`访问网关服务,网关从Eureka中通过服务名拉取实例列表,通过负载均衡选取一个实例,找到服务实例对应的真实路径,进行访问。

从输出日志中可以看到使用了负载均衡

2019-06-03 11:02:05.809  INFO 27532 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty  : Flipping property: user-server.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647

简化的路由配置

刚才的配置中:

  • zuul.routes.<route>.path=/xxx/** :来指定映射路径,<route>是自定义的路由名;
  • zuul.routes.<route>.serviceId=/user-server:来指定服务名;

而在大多数情况下,我们的路由名称往往和服务名写成一样的,因此Zuul就提供一种简化的配置语法

所以我们可以这样写:

zuul:
  routes:
    user-server: /user-server/**

默认的路由规则

在使用Zuul的过程中,上面讲述的规则已经大大简化了配置项,但是当服务较多时,配置也是比较繁琐的。因此Zuul就只定了默认的路由规则:

默认情况下,一切服务的映射路径就是服务名本身,比如:服务名为user-server,默认的映射路径就是:/user-server/** ,并且在网关启动后,会自动从Eureka中拉取所有的服务信息

这意思就说,我们完全可以不用配置路由规则。

但是对于某些服务来说只能进行服务间调用,这样拉取全部服务信息反而会降低效率,而且有的服务不希望将路径暴露给网关。

#映射规则
zuul:
  routes:
    user-server: /user-server/**
  ignored-services: 
    - user-consumer

可以使用这种配置来指定不需要网关映射的服务。

路由前缀

zuul:
  prefix: /api # 添加路由前缀
  routes:
      user-service: # 这里是路由id,随意写
        path: /user-service/** # 这里是映射路径
        service-id: user-service # 指定服务名称

路径/api/user-server/user/1将会被代理到/user-server/user/1

#映射规则
zuul:
  routes:
    user-server: /user/**  #指定匹配路径,减短字符长度
  ignored-services:
    - user-consumer

在这里插入图片描述

可以看到访问的时候由两个/user,第一个是匹配时用的,一旦匹配到真实的路径localhost:8081,就会将前面的/user去掉,通过localhost:8081/user/1来访问。所以我们可以想着不去除/user前缀这样我们就可以只通过localhost:8081/user/1来访问。这里/user既用作匹配路径,也用作真实路径。

#映射规则
zuul:
  routes:
    user-server:
      path: /user/**
      serviceId: user-server
      strip-prefix: false #表示不去除前缀
  ignored-services:
    - user-consumer

在这里插入图片描述
在这里插入图片描述

过滤器

Zuul作为网关的其中一个重要功能就是实现请求鉴权,而这个动作我们往往是通过Zuul提供的过滤器来实现的。

ZuulFilter

ZuulFilter是过滤器的顶级父类;

public abstract ZuulFilter implements IZuulFilter{

	//返回字符串,代表过滤器的类型
    abstract public String filterType();

	//通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高
    abstract public int filterOrder();
    
    //判断该过滤器是否需要执行
    boolean shouldFilter();// 来自IZuulFilter

	//具体业务逻辑
    Object run() throws ZuulException;// IZuulFilter
}

过滤器类型:

  • pre:请求在路由之前执行;
  • routing:在路由请求时调用;
  • post:在routing和error过滤器之后调用;
  • error:处理请求时发生错误调用

过滤器执行生命周期

各个过滤器的执行顺序

在这里插入图片描述

  • 正常流程:
    • 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者。执行请求,返回结果后,会到达post过滤器,而后返回响应。
  • 异常流程:
    • 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,在error处理完后,会将请求交给post过滤器,最后返回给用户。
    • 如果是error过滤器自己出现异常,最终也会进入post过滤器,而后返回;
    • 如果是post过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的是,请求不会再到达post过滤器了。

使用场景

  • 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了;
  • 异常处理:一般会在error类型和post类型过滤器中结合来处理;
  • 服务调用时长统计:pre和post结合使用;

自定义过滤器

模拟一个登录校验,基本逻辑:如果请求中有access-token参数,则认为请求有效,放行。

@Component
public class LoginFilter extends ZuulFilter {
    @Override
    public String filterType() {
        //设置这个过滤器为前置过滤器
        return "pre";
    }

    @Override
    public int filterOrder() {
        //顺序设置为1
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        //返回true 代表过滤器生效
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //过去Zuul提供的请求上下文对象
        RequestContext ctx = RequestContext.getCurrentContext();
        //从上下文中获取request对象
        HttpServletRequest request = ctx.getRequest();
        //从请求中获取token
        String token = request.getParameter("access-token");
        //判断
        if(StringUtils.isEmpty(token)){
            //没有token,拦截
            ctx.setSendZuulResponse(false);
            //返回401状态码,可以考虑重定向到登录页
            ctx.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
        }
        //校验通过,可以考虑把用户信息放入上下文,继续向后执行
        return null;
    }
}

在这里插入图片描述
在这里插入图片描述

负载均衡和熔断

Zuul中默认就已经集成了Ribbon负载均衡和Hystrixr熔断机制。但是所有超时策略都是走的默认值,比如熔断超时时间只有1000ms,很容易就被触发了。

在这里插入图片描述

配置超时

ribbon:
  ConnectTimeout: 250 #连接超时
  ReadTimeout: 2000 #通信超时时间
  OkToRetryOnAllOperations: true #是否对所有操作重试
  MaxAutoRetriesNextServer: 2 #同一服务不同实例的重试次数
  MaxAutoRetries: 1 #同一实例的重试次数 
 
hystrix:
  command: 
    default: 
      execution: 
        isolation: 
          thread: 
            timeoutInMilliseconds: 6000 #熔断超时时长 

默认超时算法:(ConnectTimeout+ReadTimeout)*2<hystrix的timeoutInMilliseconds,如果大于则会出现警告。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值