【spring cloud - zuul服务网关】


前言

接着上章【SpringCloudNetflix-Hystrix熔断器】,继续学习spring cloud - zuul服务网关。


一、zuul是什么?

Zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet(filter)应用。Zuul 在云平台上提供动态路由(请求分发),监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门,也要注册入Eureka,用一张图来理解zuul在架构中的的角色:
在这里插入图片描述
需要注意的是,zuul本身是一个独立的服务,默认集成了Ribbon,zuul通过Ribbon将客户端的请求分发到下游的微服务,所以zuul需要通过Eureka做服务发行,同时zuul也集成了Hystrix。

根据上图理解 ,我们需要建立独立的工程去搭建Zuul服务,同时需要把Zuul注册到EurekaServer,因为当请求过来时,zuul需要通过EurekaServer获取下游的微服务通信地址,使用Ribbon发起调用。

二、使用步骤

搭建zuul工程springcloud-zuul-server 端口:1060,集成EurekaClient和zuul

1.导入依赖

代码如下(示例):

		<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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

2. 配置开开启Zuul

配置类通过 @EnableZuulProxy 注解开启zuul服务功能。

@SpringBootApplication
@EnableZuulProxy      // 开启zuul 可以看做是 @EnableZuulServer 的增强版 ,一般用这个
// @EnableZuulServer  // 这个标签也可以开启zuul,但是这个标签开启的Filter更少
public class ZuulApplication {
    public static void main( String[] args )
    {
        SpringApplication.run(ZuulApplication.class, args);
    }
}

3. 配置文件配置zuul

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:1010/eureka/ #注册地址
  instance:
    instance-id: zuul-service0  #实例ID
spring:
  application:
    name: zuul-service # 客户端名字

server:
  port: 1060 #端口号

zuul:
  prefix: "/servers"    #统一访问前缀
  ignoredServices: "*"   #禁用掉使用浏览器通过服务名的方式访问服务
  routes:
    pay-service: "/pay/**"       #指定pay-server这个服务使用 /pay路径来访问  - 别名
    order-service: "/order/**"   #指定order-server这个服务使用 /order路径来访问
    user-service: "/user/**"     #指定user-service这个服务使用 /user

提示: 我们对zuul主要做了三个配置

  • zuul.prefix : 作为统一的前缀,在浏览器访问的时候需要加上该前缀
  • zuul.ignoredServices : 忽略使用服务名方式访问服务,而是通过routes指定的路径进行访问
  • zuul.routes : 配置服务的访问路径

注意:在么有使用zuul之前我们是通过 http://localhost:1040/pay/1 来直接访问支付服务,现在需要通过zuul来访问,格式如下:http:// zuul的ip : zuul的port /zuul前缀 / 服务路径 /服务的controller路径 ,即:

http://localhost:1060/servers/pay/pay/1

特别说明:其实这里我们直接浏览器也能访问到目标服务,即可以通过: http://localhost:1040/pay/1 绕过zuul,但是这种情况不用担心,因为在产品上线的时候我们都是内网部署,只有zuul我们部署成外网,也就是说直接访问目标微服务的方式是访问不到的,所以我们只需要通过zuul访问即可。

三,自定义zuul的Filter

该处使用的url网络请求的数据。

1. zuul的工作原理

zuul的底层是通过各种Filter来实现的,zuul中的filter按照执行顺序分为了“pre”前置(”custom”自定义一般是前置),“routing”路由,“post”后置,以及“error”异常Filter组成,当各种Filter出现了异常,请求会跳转到“error filter”,然后再经过“post filter” 最后返回结果,下面是Filter的执行流程图:
在这里插入图片描述

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

2. 自定义Filter

演示一个案例,在Zuul层实现统一的登录检查:如果请求头中有“token”属性,我们就认为已经登录成功,可以继续往下游的服务执行,否则就视为请求未登录,直接返回错误信息,这一需求需要自定义Filter继承ZuulFilter类来实现,具体代码如下:

@Component
public class loginFilter extends ZuulFilter {
    @Override
    public String filterType() {
        // filter类型:   前置"pre"  路由"routing" 后置 "post" 错误"error"
        return "pre";
    }

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

    @Override
    public boolean shouldFilter() {
        // 决定run方法是否执行,返回true,执行run,返回false,不执行
        // 获取请求地址,对登录和注册请求放行
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String uri = request.getRequestURI();
        if (uri.startsWith("/login")||uri.startsWith("register")){
            return false;
        }
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        // 获取请求体中的token判断是否登录,未登录响应一个json对象,登录放行
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        // 获取响应对象
        HttpServletResponse response = currentContext.getResponse();
        // 设置响应类型
        response.setContentType("application/json;charset=utf-8");
        // 获取请求体中的token数据
        String token = request.getHeader("token");
        if (!StringUtils.hasLength(token)) {
            // 不执行下游服务器,返回一个json
            currentContext.setSendZuulResponse(false);
            try {
                response.getWriter().print("请登录!!!!!");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }
}
  • 在 filterType方法中我们返回“pre”前置filter的常量,让他成为前置filter(登录检查需要在请求的最前面来做)
  • 在filterOrder方法中返回的顺序值是 0 ,执行顺序越小越先执行
  • 在shouldFilter方法中通过判断请求的url来决定是否需要做登录检查,返回true就是要做然后才会执行run方法
  • 在run方法中我们通过获取请求头中的token判断是否登录,如果没登录就返回错误信息,阻止继续执行。
  • RequestContext.getCurrentContext() 是一个Zuul提供的请求上下文对象

四,zuul的熔断器配置

zuul作为服务网关面向的是客户端(浏览器),当服务调用链路出现异常,我们不希望直接把异常信息抛给客户端,而是希望触发降级,返回友好的提示信息,所以我们需要去配置zuul的熔断机制。

在zuul中要实现熔断功能需要实现ZuulFallbackProvider接口,该接口提供了两个方法:getRoute用来指定熔断功能应用于哪些路由的服务,fallbackResponse方法为熔断功能时执行的方法(用来返回托底数据)

1. 代码实现

例子:对pay-server做一个熔断器配置,当zuul先pay-server发起调用,服务调用失败,触发熔断,返回一句话“提示信息”

代码如下(示例):

@Component
public class PayServerFallback implements FallbackProvider {
    private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class);

    // 指定要处理的服务。
    @Override
    public String getRoute() {
        return "pay-server";  //"*"代表所有服务都有作用
    }

    /**
     * @param route :服务的路由
     * @param cause : 异常
     * @return ClientHttpResponse:熔断后的换回值
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("抱歉,服务不可用".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

五. zuul的参数配置

1.超时配置

Zuul集成了hystrix,如果服务的调用链过长,或者ribbon调用事件过长,可能会触发Hystrix的熔断机制,导致请求拿不到正常的结果,我们通常会对Ribbon和Hystrix的超时时间配置。如下配置对所有消费者微服务都有用:

zuul配置文件加上如下配置:

zuul:
  retryable: true #是否开启重试功能
ribbon:
  MaxAutoRetries: 1 #对当前服务的重试次数
  MaxAutoRetriesNextServer: 1 #切换相同Server的次数
  OkToRetryOnAllOperations: false # 对所有的操作请求都进行重试,如post就不能重试,如果没做幂等处理,重试多次post会造成数据的多次添加或修改
  ConnectTimeout: 3000 #请求连接的超时时间
  ReadTimeout: 5000 #请求处理的超时时间
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10000
            #如果配置ribbon的重试,hystrix的超时时间要大于ribbon的超时时间

2.zuul的饥饿加载

为了让服务加快第一次调用,我们可以通过设置Ribbon的饥饿加载,zuul底层通过Ribbon实现负载均衡器,所以也需要指定饥饿加载,配置如下:

zuul:
  ribbon:
    eager-load.enabled: true  	# 饥饿加载

需要注意的是,zuul是通过读取路由配置来实现饥饿加载的,所以如果要让eager-load.enabled: true起作用,我们一般不会使用默认的路由方式,而是单独配置路由规则,如上配置。


总结

对 zuul服务网关 的基础知识学习和掌握基本使用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值