基于spring-cloud的微服务API网关zuul

Posted 梦中彩虹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于spring-cloud的微服务API网关zuul相关的知识,希望对你有一定的参考价值。

API网关是微服务架构中的很重要的一个部分,内部有多个不同的服务提供给外部来使用,API网关可以对外做统一的入口,也可以在网关上做协议转换,权限控制和请求统计和限流等其他的工作
spring-cloud封装了Netflix提供的开源的API网关实现zuul,我们可以很方便地启动一个zuul网关的实例,并支持向eureka进行注册,并对在eureka上已经注册的服务进行代理

使用IDEA的spring initializer来创建一个zuul项目

填写相关的group类型等信息,选择使用gradle来进行构建

选择zuul和eureka client

选择项目位置和gradle安装的位置:

在启动类上添加 @EnableZuulProxy 注解,这个注解是@EnableZuulServer的加强版,不仅标识了这是一个zuul Server 而且启用了eureka注册中心和负载均衡ribbon

启动类的代码:

package com.jiaoyiping.springcloud.zuul;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {

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

zuul最本质的功能是做反向代理,路由转发和对请求的拦截和处理,路由转发的配置在配置文件中,zuul可以做一下几种形式的转发:

将请求重定向到一个外部的URL上,
如果我们要配置,符合/baidu前缀的请求都重定向到baidu.com去,可以做如下的配置:

zuul:
  routes:
    baidu:
      path: /baidu/**
      url: http://www.baidu.com

将请求转发到内部提供的请求路径上去,使用forforward:
比如,如果我们的网关自己提供了一个以/session开头的服务,我们想让对网关的请求中以/session开头的请求都让网关自己提供的这个服务来处理,则可以进行如下的配置

zuul:
  routes:
    session:
      path: /session/**
      url: forward:/session    

对已经注册到eureka中的服务进行代理和请求转发,此时只需要提供eureka中服务的serviceid即可

 zuul:
   routes:
     provider:
       path: /provider/**
       serviceId: PROVIDER
       #(去请求目标服务的时候)是否丢掉前缀,根据需求来配置
       stripPrefix: true

完整的配置如下:

spring:
  application:
    name: zuul
  cloud:
    inetutils:
      preferred-networks: 192.168.1.
server:
  port: 8084
  tomcat:
    uri-encoding: UTF-8
  servlet:
    context-path: /
logging:
  config: classpath:logback.xml
zuul:
  routes:
    baidu:
      path: /baidu/**
      url: http://www.baidu.com
    provider:
      path: /provider/**
      serviceId: PROVIDER
      #(去请求目标服务的时候)是否丢掉前缀,根据需求来配置
      stripPrefix: true
    session:
      path: /session/**
      url: forward:/session
eureka:
  instance:
    hostname: 192.168.1.5
    prefer-ip-address: true
    instance-id: 192.168.1.5:${server.port}
  client:
    healthcheck:
      enabled: true
    registerWithEureka: true
    fetchRegistry: true
    service-url:
      defaultZone: http://127.0.0.1:8081/eureka/
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 25000

ribbon:
  MaxAutoRetries: 2
  MaxAutoRetriesNextServer: 3
  restclient:
    enabled: true

zuul中的Filter的配置,zuul中提供了三种类型的Filter,preFilter,routeFilter和postFilter,分别对应请求中的不同的阶段,针对同一个请求,有一个RequestContext对象,在三个阶段的Filter中进行共享

假设我们要开发一个统计请求时间的功能,需要在preFilter里边记录开始时间,并将整个开始时间放在RequestContext中,在postFilter里边拿到开始时间,用当前的时间减去开始时间,就是请求执行的时间

定义一个preFilter:

package com.jiaoyiping.springcloud.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

/**
 * Created with Intellij IDEA
 *
 * @author: jiaoyiping
 * Mail: jiaoyiping@gmail.com
 * Date: 2018/04/05
 * Time: 16:24
 * To change this template use File | Settings | Editor | File and Code Templates
 */

public class TimeCostPreFilter extends ZuulFilter {
    public static final String START_TIME_KEY = "start_time";
    private Logger logger = LoggerFactory.getLogger(TimeCostPreFilter.class);

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 判断是否要拦截的逻辑
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        long startTime = System.currentTimeMillis();
        RequestContext.getCurrentContext().set(START_TIME_KEY, startTime);
        return null;
    }
}

定义以postFilter:

package com.jiaoyiping.springcloud.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

/**
 * Created with Intellij IDEA
 *
 * @author: jiaoyiping
 * Mail: jiaoyiping@gmail.com
 * Date: 2018/04/05
 * Time: 16:37
 * To change this template use File | Settings | Editor | File and Code Templates
 */

public class TimeCostPostFilter extends ZuulFilter {
    private static final String START_TIME_KE = "start_time";
    private Logger logger = LoggerFactory.getLogger(TimeCostPostFilter.class);

    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

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

    @Override
    public Object run() throws ZuulException {
        long startTime = (long) RequestContext.getCurrentContext().get(START_TIME_KE);
        logger.info("请求完成,耗时{}秒", (System.currentTimeMillis() - startTime) / 1000);
        return null;
    }
}

在一个配置类中将这两个Filter注入:

package com.jiaoyiping.springcloud.zuul.config;

import com.jiaoyiping.springcloud.zuul.filter.PDSFilter;
import com.jiaoyiping.springcloud.zuul.filter.TimeCostPostFilter;
import com.jiaoyiping.springcloud.zuul.filter.TimeCostPreFilter;
import com.netflix.zuul.ZuulFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created with Intellij IDEA
 *
 * @author: jiaoyiping
 * Mail: jiaoyiping@gmail.com
 * Date: 2018/04/05
 * Time: 17:32
 * To change this template use File | Settings | Editor | File and Code Templates
 */
@Configuration
public class FilterConfig {

    @Bean
    public ZuulFilter timeCostPreFilter() {
        return new TimeCostPreFilter();
    }

    @Bean
    public ZuulFilter timeCostPostFilter() {
        return new TimeCostPostFilter();
    }


    @Bean
    public ZuulFilter pdsFilter() {
        return new PDSFilter();
    }
}

启动项目,可以发现,zuul网关已经注册到了eureka上:

请求provide对应的地址,发现,zuul可以成功地调用eureka上对应的服务,并将结果正确返回:

以上是关于基于spring-cloud的微服务API网关zuul的主要内容,如果未能解决你的问题,请参考以下文章

带有 API 网关的 AWS ECS 中基于路径的微服务路由

使用 API 网关 Ocelot 与 Nginx 的微服务

基于Spring cloud gateway定制的微服务网关

干货分享微服务spring-cloud(6.Api网关服务zuul)

使用 Azure Service Fabric 部署的微服务的 API 网关/代理模式

spring-cloud feign hystrix配置熔断为啥不生效的原因