API网关服务Zuul - Spring Cloud系列(五)

本文章基于spring-boot-starter-parent 2.0.6RELEASE,spring-cloud-dependencies Finchley.SR2。

Zuul是什么

Spring Cloud Zuul是基于Netflix Zuul实现的API网关组件,包含了对请求的路由和过滤两个做最主要的功能。其中路由功能负责将请求转发到具体的微服务实例上,是实现外部访问统一入口的基础,而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等动能的基础。

Zuul通过和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用功能,同时从Eureka中获去其它微服务的实例信息。这样的设计将服务实例的维护工作交给了服务治理框架自动完成,而对于路由规则的维护,Zuul默认会将通过以服务名为ContextPath的方式来创建路由映射,大大减少了运维工作。

API网关基本功能

  • 单点入口
  • 路由转发
  • 限流熔断
  • 日志监控
  • 安全认证

Zuul使用

创建一个Spring Boot工程,命名为zuul

第一步:引入zuul和eureka依赖

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

第二步:创建启动类,并使用@EnableZuulProxy注解开启Zuul的API网关服务功能

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

第三步:application.yml中配置Zuul应用的相关信息

server:
  port: 9000

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8081/eureka
  instance:
    instance-id: zuul-1 #此实例注册到eureka服务端的唯一的实例ID
    prefer-ip-address: true #是否显示IP地址

spring:
  application:
    name: zuul #此实例注册到eureka服务端的name

zuul:
  routes:
    myorder:
      serviceId: order-micro
      path: /order/**

完成上面的工作后,通过Zuul+eureka实现的网关服务就构建完成了。为了演示请求路由功能,我们借用之前文章中搭建起来的Eurek和Order工程。

启动Eureka、Order以及Zuul工程,进入Eurek信息面板中可以看到Zuul作为一个服务注册进来了。
eurek-zuul
在上面的application.yml配置文件中,我们看到了关于zuul路由的配置

zuul:
  routes:
    myorder:
      serviceId: order-micro
      path: /order/**

其中myorder是我们定义的路由规则的名称,path指定路由路径,所有请求url满足/order/**规则的都会被路由到serviceId指定的服务上。order-micro是Eureka中注册的服务名。

注意:路由规则中,/ **代表是所有层级, / * 是代表一层,?匹配任意单个字符

浏览器访问http://localhost:9000/order/getOrder.do(通过网关访问路径,Order服务真实的访问路径为http://localhost:7000/getOrder.do),会发现请求被正确路由了。
zuul路由order
禁用微服务名调用

浏览器访问:http://localhost:9000/order-micro/getOrder.do,我们发现通过服务名也能够访问服务,把微服务名称暴露出去,这是不合理也不安全的,所以我们一般会禁用微服务名方式的调用。

applicaion.yml中加入配置ignored-services: user-micro即可禁用某个通过微服务名调用的方式。如果配置成ignored-services: "*",代表禁用所有。

配置样式如下:

zuul:  
  ignored-services: "*"

增加路由前缀
定义接口名称一般都需要一定的规范,譬如调用微服务的API URl前缀需要加上"/api",对于这种情况只需要增加一条配置zuul.pefix=/api。如果"/api"会出现在路由后的IP地址中怎么办呢?只需要在增加一条配置zuul.strip-prefix=false即可。

配置样式如下:

zuul:
  pefix: /api 
  strip-prefix: false

请求过滤

过滤器(Filter)可以说是Zuul实现API网关功能最为核心的组件,Zuul中的大部分功能都是通过过滤器来实现的。 每一个进入Zuul的HTTP请求都会经过一系列的过滤器处理链得到请求相应并返回给客户端。

Zuul重定义了4中标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。

  • PRE: 请求被路由到源服务器之前调用
    • 认证
    • 选路由
    • 日志
  • ROUTING: 处理将请求发送到源服务器的过滤器
  • POST: 在响应从源服务器返回时被调用
    • 对响应增加HTTP头
    • 收集统计和度量
    • 将响应以流的方式发送回客户端
  • ERROR: 在上述阶段中出现错误时被调用

下面我们通过编写一个日志过滤器LogFilter,在请求被路由之前记录下请求的相关信息。实现方法很简单,只需继承ZuulFilter抽象类并实现其中的方法:

@Component
public class LogFilter extends ZuulFilter {
    @Override
    public String filterType() {
    	// pre
        return FilterConstants.ROUTE_TYPE;
    }

    @Override
    public int filterOrder() {
  	    // PreDecorationFilter,执行顺序是5,是pre阶段最后被执行的过滤器
        return FilterConstants.PRE_DECORATION_FILTER_ORDER;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String remoteAddr = request.getRemoteAddr();
        String routeAddr = currentContext.get(FilterConstants.REQUEST_URI_KEY).toString();
        System.out.println("访问地址:" + request.getRequestURI() + "路由后的地址:" + routeAddr);
        return null;
    }
}

重新运行Zuul工程,访问http://localhost:9000//getOrder.do,查看下控制台,可看到打印信息:
logfilter
在上面的代码中,我们通过集成ZuulFilter抽象类并重写了下面4个方法,这四个方法分别定义了如下内容:

  • filterType: 过滤器的类型,有pre、route、post、error等几种取值,它决定过滤器在请求的哪个生命周期中执行。
  • filterOrder: 返回一个int值来指定过滤器的执行顺序,不同的过滤器允许返回相同的数字。
  • shouldFilter: 返回一个boolean值来判断该过滤器是否要执行,ture表示执行。
  • run: 过滤器的具体逻辑

Spring Cloud Zuul默认为Zuul编写了一些过滤器,这些过滤器默认都是启用状态的(包括我们自己编写的),如果不想使用了,如何禁用呢?
配置格式:zuul.<SimpleClassName>.<filterType>.disable=true,比如禁用我们上面写的过滤器zuul.LogFilter.pre.disable=true

Hystrix与Ribbon支持

Zuul默认是整合了Hystrix和Ribbon的,所以Zuul天生就拥有线程隔离和断路器的自我保护功能,以及对服调用的客户端负载均衡功能。我们可以通过Hystrix和Ribbon的参数来调整路由请求的各种超时时间配置。

启动Eureka、zuul以及客户端负载均衡Ribbon文章中使用的两个Order工程,可以发现Zuul默认开启了Ribbon负载均衡。
zuul1
zuul2

下面来看下Zuul如何使用功能Hystrix中的降级回退:
停掉上面启动的两个Order工程,只启动Zuul和Eureka,浏览器访问http://localhost:9000/order/getOrder.do会发现返回报错页面,这肯定是不友好的。
未降级
现在我们来实现Hystrix中的降级回退,继承FallbackProvider类然后重写里面的方法:

@Component
public class MyFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        // 指定为哪个微服务提供回退(微服务名 写*代表所有微服务)
        return "order-micro";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    private ClientHttpResponse response(final HttpStatus status) {
        // 这里返回一个ClientHttpResponse对象并实现其中的方法
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return status;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                // 返回status的code 比如 404,500等
                return status.value();
            }

            @Override
            public String getStatusText() throws IOException {
                // 返回一个HttpStatus对象的reasonPhrase信息
                return status.getReasonPhrase();
            }

            @Override
            public void close() {
                //降级信息全部响应完了之后调用的方法
            }

            @Override
            public InputStream getBody() throws IOException {
                // 把降级信息响应回前端
                return new ByteArrayInputStream("Zuul降级信息".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                // 需要对响应报头设置的话可以在此设置
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

再次访问http://localhost:9000/order/getOrder.do发现服务被正确降级了
zuul降级
参考资料
Providing Hystrix Fallbacks For Routes


------------本文结束感谢您的阅读------------
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值