SpringCloud Netflix复习之Zuul

写作背景

本文是继复习了Eureka、Ribbon、OpenFeign、Hystrix之后,SpringCloud系列的微服务网关组件部分。
SpringCloud Zuul虽然现在用的比较少,基本都被SpringCloud Gateway替代了,但是因为公司的老项目中还是用的Zuul,因此有必要再复习下。
本文的写作思路主要包括以下几个方面来,主要是实战和源码验证

  1. Zuul是什么?
  2. Zuul的核心功能
  3. 上手实战
  4. 从源码角度验证下核心功能

Zuul是什么

Zuul本身是Netflix公司研发的网关组件,1.x的版本底层是基于阻塞式IO(Zuul 2.x版本基于Netty性能也很高)SpringCloud Zuul就是SpringCloud官方基于Zuul做了一层封装。说到网关,那么先回想一下之前的实战中没有网关,写在fc-service-portal和fc-service-screen里的接口,要想访问他们这两个服务里的接口,需要切换对应的服务的ip和端口。这还只是个示例,真正生产环境中,微服务的个数是很多的,不可能让前端访问后端的接口要记住那么多的host,那前端哥们会疯掉。
网关就是让前端所有请求都先路由到网关,由网关和Ribbon,Hystrix等整合决定访问路由下游具体哪一个服务实例。如果网关要做高可用,其实很简单,只需要把网关部署多个实例,然后用Nginx做负载均衡,此时的架构就是
前端 => 负载均衡 => 网关 => 后端服务

Zuul的核心功能

Zuul在SpringCloud中的角色定位就是请求路由,然后解析请求URI,再根据你application.yml里配置的路由规则,进行路由匹配,然后将请求封装一下,基于Eureka + Ribbon实现服务的负载均衡,基于Hystirx包裹实际的请求,完成熔断降级就转发到对应的服务。
然后就是有很多过滤器

1、执行请求前阶段的pre过滤器
ServletDetectionFilter
Servlet30WrapperFilter
FromBodyWrapperFilter
DebugFilter
PreDecorationFilter

2、请求路由阶段的routing过滤器
RibbonRoutingFilter
SimpleHostRoutingFilter
SendForwardFilter

3、执行请求后返回结果前阶段的post过滤器
SendResponseFilter

4、执行错误阶段的error过滤器
SendErrorFilter

上手实战

SpringCloud中Zuul如何使用

新建一个fc-gateway-zuul的工程项目
1、pom.xml引入坐标依赖

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

2、启动类增加@EnableZuulProxy注解开启Zuul功能

@SpringBootApplication
@EnableZuulProxy
public class FcGatewayZuulApplication {
   

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

}

3、配置文件application配置路由到fc-service-portal里的

server:
  port: 8000

spring:
  application:
    name: fc-gateway-zuul

#eureka相关配置
eureka:
  client:
    service-url:
      defaultZone: http://root:123456@localhost:8761/eureka/
  instance:
    #显示的微服务名称
    instance-id: ms-fc-gateway-zuul-8000
    #eureka客户端向服务端发送心跳时间默认30s
    lease-renewal-interval-in-seconds: 10
    #Eureka服务器在接收到实例的最后一次发出的心跳后,需要等待多久才可以将此实例删除,默认为90秒
    lease-expiration-duration-in-seconds: 30    

#路由配置
zuul:
  routes:
    #主要用来唯一标识一个path的,一般用服务名
    fc-service-portal:
      path: /portal/**

启动ureka-server,fc-gateway-zuul,fc-service-portal,fc-service-screen
然后通过fc-gateway-zuul来访问fc-service-portal的一个接口

http://localhost:8000/portal/getUser3/2?age=27

在这里插入图片描述
看接口访问的结果,是预期的
在这里插入图片描述
说明fc-gateway-portal的路由效果起到了。

自定义过滤器

自定义一个继承ZuulFilter的MyZuulFilter类

@Slf4j
public class MyZuulFilter extends ZuulFilter {
   
    @Override
    public String filterType() {
   
        //在哪个阶段执行
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
   
        //数字越小优先级越大
        return 1;
    }

    @Override
    public boolean shouldFilter() {
   
        //是否需要执行过滤器,默认是false
        return true;
    }

    @Override
    public Object run() throws ZuulException {
   
        log.info("执行MyZuulFilter逻辑");
        return null;
    }
}

然后注入到Spring容器

@Configuration
public class MyZuulFilterConfig {
   

    @Bean
    public MyZuulFilter zuulFilter() {
   
        return new MyZuulFilter();
    }
}

我们启动服务,然后访问一个接口看看效果

http://localhost:8000/portal/getPortByFeign

在这里插入图片描述

配置全局Fallback降级

Zuul与Ribbon整合会用到RibbonRoutingFilter过滤器,转发的时候会用Hystrix包裹请求,如果请求失败会执行fallback逻辑。
定义一个实现了FallbackProvider接口的MyFallbackProvider类,然后通过@Bean注入到Spring容器。

import com.netflix.hystrix.exception.HystrixTimeoutException;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

public class MyFallbackProvider implements FallbackProvider {
   

    @Override
    public String getRoute() {
   
        //也可以针对某一个服务配置,比如fc-service-portal
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
   
        //这里可以根据实际的异常做不同的处理,hystrix默认超时时间是1s
        if (cause instanceof HystrixTimeoutException) {
   
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
   
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    private ClientHttpResponse response(final HttpStatus status) {
   
        return new ClientHttpResponse() {
   
            @Override
            public HttpStatus getStatusCode() throws IOException {
   
                return status;
            }

            @Override
            public int getRawStatusCode() throws IOException {
   
                return status.value();
            }

            @Override
            public String getStatusText() throws IOException {
   
                return HttpStatus.BAD_REQUEST.getReasonPhrase();
            }

            @Override
            public void close() {
   
            }

            @Override
            public InputStream getBody() throws IOException {
   
                return new ByteArrayInputStream(("fallback:" + MyFallbackProvider.this.getRoute()).getBytes());
            }

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

注入Spring容器

@Configuration
public class HystrixFallbackConfig {
   

    @Bean
    public FallbackProvider myFallbackProvider() {
   
        return new MyFallbackProvider();
    }
}

如果Ribbon和Hystrix的超时时间都不配置,那么默认1s超时熔断,可以测试下,我们让接口睡眠1s

@GetMapping("/getPortByFeign")
    public int getPortByFeign
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值