微服务开发实战

路由网关Zuul

2019-06-07  本文已影响3人  董二弯

本章讲解构建微服务的另一个组件——智能网关组件Zuul。Zuul用于构建边界服务,致力于动态路由、过滤、监控、弹性伸缩和安全。

为什么需要Zuul

工作原理

image.png

如图,zuul的核心是一系列过滤器,对自定义的ZuulServlet(类似于spring mvc的DispatcherServlet)来对请求进行控制,经过过滤后返回请求。Zuul包括以下4种过滤器:

案列实战

搭建Zuul服务

在之前项目上新建一个Modul——api-gateway。

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ApiGatewayApplication {

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

}

spring:
  application:
    name: api-gateway
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
server:
  port: 5000
zuul:
  routes:
    hiapi:
      path: /hiapi/**
      serviceId: eureka-client

重点说Zuul的配置写法。zuul.routes.hiapi.path: /hiapi/**,zuul.routes.hiapi.serviceId:eureka-client。表示可以将 “/hiapi”开头的url路由到eureka-client,其中的“hiapi”时自定义的。如果服务存在多个实例,Zuul会结合Ribbon做负载均衡。

多次访问 http://localhost:5000/hiapi/hi?name = dzy
会交替出现
      hi dzy ,I am from 8763
      hi dzy ,I am from 8764
可见实现了路由并做了负载均衡。

注意
如果不需要负载均衡,可以指定服务实例的Url,用zuul.routes.hiapi.url配置,配置后直接访问指定的Url,在实际开发中很少用到。

zuul:
  routes:
    hiapi:
      path: /hiapi/**
      url: http://localhost:8763

如果想指定Url,并且想做负载均衡,可以自己维护负载均衡的列表

zuul:
  routes:
    hiapi:
      path: /hiapi/**
      serviceId: hiapi-v1  # 自定义服务名
  ribbon:
    eureka:
      enabled: false   # 不从Eureka Client获取服务注册列表信息。
  hiapi-v1:   #使用自定义服务名,配置多个负载均衡的url
    ribbon:
      listOfServers: http://localhost:8763,http://localhost:8764

如果想给服务的api接口加前缀,例如http://localhost:5000/v1/hiapi/hi?name = dzy,加一个v1作为版本号。这时需要用zuul.perfix的配置

zuul:
  routes:
    hiapi:
      path: /hiapi/**
      serviceId: eureka-client
  prefix: /v1

在Zuul上配置熔断器

需要实现ZuulFallbackProvider接口。实现改接口中的两个办法,一个是getRoute()方法,用于指定熔断功能用于那些路由的服务;另一个是fallbackResponse()为进入熔断功能是执行的逻辑。ZuulFallbackProvider源码如下

public interface ZuulFallbackProvider {

    /**
     * The route this fallback will be used for.
     * @return The route the fallback will be used for.
     */
    public String getRoute();

    /**
     * Provides a fallback response.
     * @return The fallback response.
     */
    public ClientHttpResponse fallbackResponse();
}

现实现一个针对eureka-client服务的熔断器,当eureka-client的服务出现故障时,进入熔断逻辑。代码如下

@Component
public class HystrixProvider implements ZuulFallbackProvider {

    @Override
    public String getRoute() {
       //所有的路由都加熔断功能,返回*
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse() {
        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("error! I am fallback".getBytes());
            }

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

注意:这里一定要加@Component把熔断器注入IOC容器。

Zuul使用过滤器

之前讲了Zuul的工作原理就是一系列过滤器。下面讲解如何实现一个自定义的过滤器。只需要继承ZuulFilter,并实现ZuulFilter中的抽象方法和IZuulFilter接口中的方法,其中filterType()为过滤器的类型;filterOrder()为过滤顺序,为一个Int类型的值,值越小,越早执行。shouldFilter()表示是否执行过滤逻辑,true则执行run()方法,false则不执行run()方法。其中run()写具体的过滤的逻辑。
现在实现检查请求中的参数是否传了token这个参数,如果没有传,请求不被路由到具体的服务实例,直接返回响应。代码如下:

@Component
public class MyFilter extends ZuulFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger(MyFilter.class);

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

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

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

    @Override
    public Object run() {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String token = request.getParameter("token");
        if (StringUtils.isEmpty(token)) {
            LOGGER.info("token is not exist");
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(401);
            try {
                context.getResponse().getWriter().write( "token is required");
            } catch (IOException e) {
                LOGGER.error(e.getMessage());
            }
        }
        return null;
    }
}

注意:这里一定要加@Component把filter注入IOC容器。

总结

在这一章学习了搭建了Zuul服务,并介绍了Zuul的工作原理和添加熔断器、过滤器。在下一章学习构建微服务中的配置中心——Spring Cloud Config组件。
PS:项目github地址

上一篇下一篇

猜你喜欢

热点阅读