Spring Cloud Zuul 快速入门

服务网关和Zuul

为什么要有服务网关:

我们都知道在微服务架构中,系统会被拆分为很多个微服务。那么作为客户端要如何去调用这么多的微服务呢?难道要一个个的去调用吗?很显然这是不太实际的,我们需要有一个统一的接口与这些微服务打交道,这就是我们需要服务网关的原因。

我们已经知道,在微服务架构中,不同的微服务可以有不同的网络地址,各个微服务之间通过互相调用完成用户请求,客户端可能通过调用N个微服务的接口完成一个用户请求。比如:用户查看一个商品的信息,它可能包含商品基本信息、价格信息、评论信息、折扣信息、库存信息等等,而这些信息获取则来源于不同的微服务,诸如产品系统、价格系统、评论系统、促销系统、库存系统等等,那么要完成用户信息查看则需要调用多个微服务,这样会带来几个问题:

  1. 客户端多次请求不同的微服务,增加客户端代码或配置编写的复杂性
  2. 认证繁杂,访问每个服务都要进行一次认证
  3. 每个服务都通过http访问,导致http请求增加,效率不高拖慢系统性能
  4. 多个服务存在跨域请求问题,处理起来比较复杂

如下图所示:

我们该如何解决这些问题呢?我们可以尝试想一下,不要让前端直接知道后台诸多微服务的存在,我们的系统本身就是从业务领域的层次上进行划分,形成多个微服务,这是后台的处理方式。对于前台而言,后台应该仍然类似于单体应用一样,一次请求即可,于是我们可以在客户端和服务端之间增加一个API网关,所有的外部请求先通过这个微服务网关。它只需跟网关进行交互,而由网关进行各个微服务的调用。

这样的话,我们就可以解决上面提到的问题,同时开发就可以得到相应的简化,还可以有如下优点:

  1. 减少客户端与微服务之间的调用次数,提高效率
  2. 便于监控,可在网关中监控数据,可以做统一切面任务处理
  3. 便于认证,只需要在网关进行认证即可,无需每个微服务都进行认证
  4. 降低客户端调用服务端的复杂度

这里可以联想到一个概念,面向对象设计中的门面模式,即对客户端隐藏细节,API网关也是类似的东西,只不过叫法不同而已。它是系统的入口,封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、缓存、负载均衡、流量管控、路由转发等等。示意图:

总结一下,服务网关大概就是四个功能:统一接入、流量管控、协议适配、安全维护。而在目前的网关解决方案里,有Nginx+ Lua、Kong、Tyk以及Spring Cloud Zuul等等。这里以Zuul为例进行说明,它是Netflix公司开源的一个API网关组件,Spring Cloud对其进行二次封装做到开箱即用。同时,Zuul还可以与Spring Cloud中的Eureka、Ribbon、Hystrix等组件配合使用。

可以说,Zuul实现了两个功能,路由转发和过滤器:

  • 路由转发:接受请求,转发到后端服务
  • 过滤器:提供一系列过滤器完成权限、日志、限流等切面任务。
  • 可以说路由+过滤器=Zuul

服务网关的要素:

  • 网关作为唯一的入口,所以稳定性和高可用是跑不了了
  • 以及具备良好的并发性能
  • 安全性,确保服务不被恶意访问
  • 扩展性,网关容易成为吞吐量的瓶颈,所以需要便于扩展

Zuul的四种过滤器API:

  • 前置(Pre)
  • 路由(Route)
  • 后置(Post)
  • 错误(Error)

zuul前后置过滤器的典型应用场景:

  • 前置(Pre)

    • 限流
    • 鉴权
    • 参数校验调整
  • 后置(Post)
    • 统计
    • 日志

Zuul的核心是一系列过滤器,开发者通过实现过滤器接口,可以做大量切面任务,即AOP思想的应用。Zuul的过滤器之间没有直接的相互通信,而是通过本地ThreadLocal变量进行数据传递的。Zuul架构图:

在Zuul里,一个请求的生命周期:


Zuul:路由转发,排除和自定义

本小节我们来学习如何使用服务网关,也就是Spring Cloud Zuul这个组件,首先新建一个项目,选择如下模块:

pom.xml配置的依赖如下:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
    <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-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

项目创建好后,将application.properties改为bootstrap.yml,编辑内容如下:

spring:
  application:
    name: api-gateway
  cloud:
    config:
      discovery:
        enabled: true
        service-id: CONFIG
      profile: dev
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

注:我这里使用了配置中心,若对此不熟悉的话,可以参考我另一篇文章:Spring Cloud Config - 统一配置中心

在启动类中,加上@EnableZuulProxy注解,代码如下:

package org.zero.springcloud.apigateway;

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

@SpringBootApplication
@EnableZuulProxy
public class ApiGatewayApplication {

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

完成以上配置后启动这个项目,我这里项目启动是正常的。然后我们来通过这个网关访问一下商品服务中获取商品列表的接口。如下:

访问地址说明:

  • 该zuul项目跑在8951端口上
  • 第一个/product是需要访问的服务的名称
  • 后面跟的/buyer/product/list是商品服务中获取商品列表的接口地址

只要是在eureka上注册的服务都能够通过zuul进行转发,例如我通过zuul来访问config的配置文件:

如上,可以看到,报错了,网关超时。这是因为默认情况下,zuul的熔断机制超时时间是2秒,当一个服务响应的时间较长就会报网关超时错误。

我们在配置文件中,加上如下超时时间的配置即可:

ribbon.ReadTimeout, ribbon.SocketTimeout这两个就是ribbon超时时间设置,当在yml写时,应该是没有提示的,给人的感觉好像是不是这么配的一样,其实不用管它,直接配上就生效了。

还有zuul.host.connect-timeout-millis, zuul.host.socket-timeout-millis这两个配置,这两个和上面的ribbon都是配超时的。区别在于,如果路由方式是serviceId的方式,那么ribbon的生效,如果是url的方式,则zuul.host开头的生效。(此处重要!使用serviceId路由和url路由是不一样的超时策略)

如果你在zuul配置了熔断fallback的话,熔断超时也要配置,即hystrix那段配置。不然如果你配置的ribbon超时时间大于熔断的超时,那么会先走熔断,相当于你配的ribbon超时就不生效了。

现在重启项目,再次访问之前的地址,就不会出现网关超时的错误了:

之前我们访问的都是GET类型的接口,我们来看看POST类型的是否能够正常访问。如下:

每次请求某个服务的接口,都需要带上这个服务的名称。有没有办法可以自定义这个规则呢?答案是有的,在配置文件中,增加路由的自定义配置:

zuul:
  routes:
    myProduct:
      path: /myProduct/**
      serviceId: product

说明:

  • myProduct 自定义的前缀
  • path 匹配的地址
  • product 路由到哪个服务

重启项目,测试如下:

在项目启动的时候,我们也可以在控制台中查看到zuul所有的路由规则:

如果我们有些服务的接口不希望对外暴露,只希望在服务间调用,那么就可以在配置文件中,增加路由排除的配置。例如我不希望listForOrder被外部访问,则在配置文件中,增加如下配置即可:

zuul:
  ...
  ignored-patterns:
    - /myProduct/buyer/product/listForOrder
    - /product/buyer/product/listForOrder

重启项目,这时访问就会报404了。如下:

还可以使用通配符进行匹配。如下示例:

zuul:
  ...
  ignored-patterns:
    - /**/buyer/product/listForOrder

Zuul:Cookie和动态路由

我们在web开发中,经常会利用到cookie来保存用户的登录标识。但我们使用了zuul组件后,默认情况下,cookie是无法直接传递给服务的,因为cookie默认被列为敏感的headers。所以我们需要在配置文件中,将sensitiveHeaders的值置空。如下:

zuul:
  ...
  routes:
    myProduct:
      path: /myProduct/**
      serviceId: product
      sensitiveHeaders:  # 置空该属性的值即可

我们每次配置路由信息都需要重启项目,显得很麻烦,在线上环境也不能这样随便重启项目。所以我们得实现动态路由的功能,实现动态路由其实就利用一下我们之前实现的动态刷新配置文件的功能即可。首先把Zuul路由相关的配置剪切到git上,如下:

注:我这里使用了配置中心,若对此不熟悉的话,可以参考我另一篇文章:Spring Cloud Config - 统一配置中心

在pom.xml文件中,增加如下依赖项:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-client</artifactId>
</dependency>

然后在bootstrap.yml中,增加rabbitmq的配置。如下:

spring:
  application:
    name: api-gateway
  cloud:
    config:
      discovery:
        enabled: true
        service-id: CONFIG
      profile: dev
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: admin
    password: admin
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

最后在项目中创建一个config包,在该包中创建一个ZuulConfig配置类,用于加载配置文件中的配置。代码如下:

package org.zero.springcloud.apigateway.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.stereotype.Component;

/**
 * @program: api-gateway
 * @description: 网关路由配置类
 * @author: 01
 * @create: 2018-08-25 15:51
 **/
@Component
public class ZuulConfig {

    @RefreshScope
    @ConfigurationProperties("zuul")
    public ZuulProperties zuulProperties(){
        return new ZuulProperties();
    }
}

完成以上配置后,重启项目,即可实现动态路由了,例如我现在把myProduct改成yourProduct,如下:

此时无需重启项目,访问新的地址即可。如下:


Zuul的高可用

  • 因为Zuul也属于一个微服务,所以我们将多个Zuul节点注册到Eureka Server即可实现Zuul的高可用性
  • 将Nginx和Zuul “混搭”,利用nginx做负载均衡,转发到多个Zuul上

原文地址:http://blog.51cto.com/zero01/2173327

时间: 2024-08-19 21:20:28

Spring Cloud Zuul 快速入门的相关文章

笔记:Spring Cloud Zuul 快速入门

Spring Cloud Zuul 实现了路由规则与实例的维护问题,通过 Spring Cloud Eureka 进行整合,将自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获取了所有其他微服务的实例信息,这样的设计非常巧妙的将服务治理体系中维护的实例信息利用起来,使得维护服务实例的工作交给了服务治理框架自动完成,而对路由规则的维护,默认会将通过以服务名作为 ContextPath 的方式来创建路由映射,也可以做一些特别的配置,对于签名校验.登录校验等在微服务架构中的冗余问题

spring cloud zuul filter 设置过滤请求

利用spring cloud zuul 设置spring boot 中的filter ,出现了跨域问题,然后根据设置response可解决跨域,同时完成过滤请求 ********************************************************************************************************************* //主要执行的类public class PreRequestLogFilter extends Zu

spring cloud zuul

spring cloud zuul官网:http://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/1.3.1.RELEASE/,具体配置不记 spring cloud zuul是配置路由的,我们配置的时候,喜欢这么配置例如: zuul: routes: api-a: path: /api-a/** serviceId: service-A 但是其实我们不配置也是可以路由的,如果我们的api-gateway网关的开的端口是555

Spring Cloud Zuul 实现路由,获取参数

Spring Cloud Zuul作为网关,可实现请求验证过滤,路由到具体服务的作用. 举例: 比如请求订单接口,报文包括验证信息及具体请求参数. 通过配置路由到具体接口,通过参数调用接口,返回具体报文. 参数传递: 1.post请求,String类型json 2.接口获取@ResponseBody,将获取到String转json,再获取详细参数信息 验证:继承ZuulFilter类,重写run方法,调用具体验证服务,验证通过返回true.

Spring Cloud Zuul 添加 ZuulFilter

紧接着上篇随笔Spring Cloud Zuul写,添加过滤器,进行权限验证 1.添加过滤器 package com.dzpykj.filter; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Component; import com.netflix.zuul.ZuulFilter; import com.netfli

Spring Cloud Zuul性能调整

Spring Cloud 版本: Dalston.SR5 这两天通过JMeter测了一下Spring Cloud Zuul的性能,用的是两台虚机8核8G和4核8G,宿主机是10核逻辑20核,代理的服务简单的返回字符串hello,vm堆内存1G够用 先说一下测试情况,值得一提的是测试并不严谨,因为用的是虚机,并且虚机上还跑了一些其它的东西,所以不能作为最终指导,仅供参考. 8核心的情况下: zuul的性能约是nginx(8个worker)的75%, nginx 8个worker 的cup总占用率为

spring cloud zuul参数调优

zuul 内置参数 zuul.host.maxTotalConnections 适用于ApacheHttpClient,如果是okhttp无效.每个服务的http客户端连接池最大连接,默认是200. zuul.host.maxPerRouteConnections 适用于ApacheHttpClient,如果是okhttp无效.每个route可用的最大连接数,默认值是20. zuul.semaphore.max-semaphores Hystrix最大的并发请求execution.isolati

Spring Cloud Zuul中使用Swagger汇总API接口文档

有很多读者问过这样的一个问题:虽然使用Swagger可以为Spring MVC编写的接口生成了API文档,但是在微服务化之后,这些API文档都离散在各个微服务中,是否有办法将这些接口都整合到一个文档中? 如果您还不了解Spring Cloud Zuul和Swagger,建议优先阅读下面两篇,有一个初步的了解: Spring Cloud构建微服务架构:服务网关(基础) Spring Boot中使用Swagger2构建强大的RESTful API文档 准备工作 上面说了问题的场景是在微服务化之后,所

SpringCloud---API网关服务---Spring Cloud Zuul

1.概述 1.1 微服务架构出现的问题   及  解决: 1.1.1 前言 每个微服务应用都提供对外的Restful API服务,它通过F5.Nginx等网络设备或工具软件实现对各个微服务的路由与负载均衡,并公开给外部客户端使用: 1.1.2 出现的问题 运维人员角度: 开发人员角度: 为了保证对外服务的安全性,在服务端实现都会加入一定的权限校验,同时为了安全,还会有签名校验等: 这样导致每个微服务应用都需要有冗余的代码,后期维护量非常大: 1.1.3 解决 为了解决这些常见的架构问题,API网