Spring Cloud Zuul 基础搭建

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
简介: 通过前几篇文章的介绍,我们了解了Spring Cloud Eureka 如何搭建注册中心,Spring Cloud Ribbon 如何做负载均衡,Spring Cloud Hystrix 断路器如何保护我们的服务,以防止雪崩效应的出现,Spring Cloud Feign进行声明式服务调用都有哪些应用,相比Ribbon和Hystrix都有哪些改善。可以说,以上几个组件都是搭建一套微服务架构所必须的。通过以上思路,能够梳理出下面这种基础架构:

Spring Cloud Zuul API服务网关一、Zuul 介绍二、构建Spring Cloud Zuul网关构建网关请求路由传统路由方式面向服务的路由请求过滤

一、Zuul 介绍

通过前几篇文章的介绍,我们了解了Spring Cloud Eureka 如何搭建注册中心,Spring Cloud Ribbon 如何做负载均衡,Spring Cloud Hystrix 断路器如何保护我们的服务,以防止雪崩效应的出现,Spring Cloud Feign进行声明式服务调用都有哪些应用,相比Ribbon和Hystrix都有哪些改善。可以说,以上几个组件都是搭建一套微服务架构所必须的。通过以上思路,能够梳理出下面这种基础架构:

27.jpg


无服务网关的架构图

在此架构中,我们的服务集群是内部ServiceAServiceB,他们都会向Eureka Server集群进行注册与订阅服务。而OpenService是一个对外的Restful API 服务,它通过F5,Nginx等网络设备或工具软件实现对各个微服务的路由与负载,公开给外部客户端调用

那么上述的架构存在什么问题呢?从运维的角度来看,当客户端单机某个功能的时候往往会发出一些请求到后端,这些请求通过F5,Nginx等设施的路由和负载均衡分配后,被转发到各个不同的实例上,而为了让这些设施能够正确的路由与分发请求,运维人员需要手动维护这些实例列表,当系统规模增大的时候,这些看似简单的维护回变得越来越不可取。从开发的角度来看,为了保证服务的安全性,我们需要在调用内部接口的时候,加一层过滤的功能,比如权限的校验,用户登陆状态的校验等;同时为了防止客户端在请求时被篡改等安全方面的考虑,还会有一些签名机制的存在。

正是由于上述架构存在的问题,API网关被提出,API网关更像是一个智能的应用服务器,它的定义类似于设计模式中的外观模式,它就像是一个门面的角色,结婚时候女方亲属堵门时候的角色,我去参加婚礼当伴郎的时候去村子里面见新娘,女方亲属会把鞋子藏起来,有可能藏在屋子里有可能藏在身上,这得需要你自己去寻找,找到了鞋子之后,你才能够给新娘穿上才能正式的会见家长。API网关真正实现的功能有请求路由负载均衡校验过滤请求转发的熔断机制服务的聚合等一系列功能。

Spring Cloud Zuul通过与Spring Cloud Euerka进行整合,将自身注册为Eureka服务治理下的应用,同时从Eureka中获得了所有的微服务的实例信息。者可以通过使用Zuul来创建各种校验过滤器,然后指定哪些规则的请求需要执行校验逻辑,只有通过校验的才会被路由到具体的微服务接口。下面我们就来搭建一下Spring Cloud Zuul服务网关

二、构建Spring Cloud Zuul网关

下面我们就来实际搭建一下Zuul网关,来体会一下网关实际的用处

构建网关

在实现各种API网关服务的高级功能之前,我们先来启动一下前几章搭建好的服务server-providerfeign-consumereureka-server,虽然之前我们一直将feign-consumer视为消费者,但是在实际情况下,每个服务既时服务消费者,也是服务提供者,之前我们访问的http://localhost:9001/feign-consumer等一系列接口就是它提供的服务。这里就来介绍一下详细的构建过程

  • 创建一个Spring Boot功能,命名为api-gateway,并在Pom.xml文件中引入如下内容
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.api.gateway</groupId>
    <artifactId>api-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>api-gateway</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
            <version>1.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Brixton.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

对于spring-cloud-starter-zuul 依赖,可以通过查看依赖配置了解到,它不仅包含了Netflix Zuul的核心依赖zuul-core,还包括了下面这些网关的重要依赖

28.jpg


  • spring-cloud-starter-hystrix: 该依赖用在网关服务中实现对微服务转发时候的保护机制,通过线程隔离和断路器,防止因为微服务故障引发的雪崩效应
  • spring-cloud-starter-ribbon: 该依赖用在实现网关服务进行负载均衡和请求重试
  • spring-cloud-starter-actuactor: 该依赖用来提供常规的微服务管理端点。另外,Spring Cloud Zuul 中还特别提供了/routes端点来返回当前的路由规则
  • 在ApiGatewayApplication 主入口中添加@EnableZuulProxy注解开启服务网关功能
@EnableZuulProxy

@SpringBootApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
  • 在application.properties 中配置Zuul应用的基础信息,包括应用名,端口号,具体如下
spring.application.name=api-gateway
server.port=5555

请求路由

下面,我们通过一个简单的示例来为上面构建的网关增加请求路由的功能,为了演示请求路由的功能,我们先将之前的Eureka服务注册中心和微服务应用都启动起来。观察下面的服务列表,可以看到两个微服务应用已经注册成功了

29.jpg

传统路由方式

使用Spring Cloud Zuul实现路由功能非常简单,只需要对api-gateway服务增加一些关于路由的配置规则,就能实现传统路由方式

zuul.routes.api-a-url.path=/api-a-url/**
# 映射具体的url路径
zuul.routes.api-a-url.url=http://localhost:8080/

该配置定义了发往API网关服务的请求中,所有符合/api-a-url/** 规则的访问都将被路由转发到 http://localhost:8080 的地址上,也就是说,当我们访问http://localhost:5555/api-a-url/hello 的时候,API网关服务会将该请求路由到http://localhost:8080/hello 提供的微服务接口中。其中,配置属性zuul.routes.api-a-url.path 中的api-a-url部分为路由的名字,可以任意定义,但是一组path和url映射关系的路由名要相同

面向服务的路由

很显然,传统的配置方式对我们来说并不友好,他同样需要运维人员花费大量的时间维护各个路由path 和url的关系。为了解决这个问题,Spring Cloud Zuul实现了与Spring Cloud Eureka的无缝衔接,我们可以让路由的path不是映射具体的url,而是让它映射到具体的服务,而具体的url则交给Eureka的服务发现机制去自动维护

  • 为了实现与Eureka的整合,我们需要在api-gateway的pom.xml中引入spring-cloud-starter-eureka依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
  • 在api-gateway服务中对应的application.properties文件中加入如下代码
zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=server-provider
zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.serviceId=feign-consumer
eureka.client.service-url.defaultZone=http://localhost:1111/eureka/

针对我们之前准备的两个微服务应用server-providerfeign-consumer,在上面的配置中分别定义了api-a 和 api-b 的路由来映射它们。然后这个api-gateway的默认注册中心是默认注册中心地址

  • 完成上述配置后,我们可以将四个服务启动起来,分别是eureka-server, server-provider, feign-consumer, api-gateway服务,启动完毕,会在eureka-server信息面板中看到多了一个api-gateway网关服务。

30.jpg

  • http://localhost:5555/api-a/hello: 这个接口符合 /api-a/的规则,由api-a 路由负责转发,该路由映射的serviceId 为 server-provider,所以最终/hello请求会被发送到server-provider服务的某个实例上去
  • http://localhost:9001/api-b/feign-consumer: 这个接口符合 /api-b/的规则,由api-b 进行路由转发,实际的地址由Eureka负责映射,该路由的serviceId是feign-consumer, 所以最终 /feign-consumer 请求会被路由到 feign-consumer 服务上。

请求过滤

在实现了请求路由功能之后,我们的微服务应用提供的接口就可以通过统一的API网关入口被客户端访问到了,但是,每个客户端用户请求微服务应用提供的接口时,它们的访问权限往往都有一定限制。为了实现客户端请求的安全校验和权限控制,最简单和粗暴的方法就是为每个微服务应用都实现一套用于校验签名和鉴别权限的过滤器或拦截器。但是,这样的方法并不可取,因为同一个系统中会有很多校验逻辑相同的情况,最好的方法是将这些校验逻辑剥离出去,构成一个独立的服务。

对于上面这种问题,更好的做法是通过前置的网关服务来完成非业务性质的校验。为了在API网关中实现对客户端请求的校验,我们将继续介绍Spring Cloud Zuul的另外一个核心功能:请求过滤,实现方法比较简单,我们只需要继承ZuulFilter抽象类并实现它定义的4个抽象函数即可

下面的代码定义了一个简单的Zuul过滤器,它实现了在请求被路由之前检查HttpServletRequest中是否带有accessToken参数

public class AccessFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
/**
* 过滤器的执行时序
* @return
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器的执行顺序
* @return
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 判断过滤器是否应该执行
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器的具体执行逻辑
* @return
*/
@Override
public Object run() {
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
log.info("send {} request to {}", request.getMethod(),request.getRequestURL().toString());
String accessToken = request.getParameter("accessToken");
if(null == accessToken){
log.warn("access token is null");
rc.setResponseStatusCode(401);
rc.setSendZuulResponse(false);
}
log.info("access token ok");
return null;
}
}

在上面实现的过滤器代码中,我们通过继承ZuulFilter 抽象类并重写了四个方法

  • filterType : 过滤器类型,它决定过滤器的请求在哪个生命周期中执行,这里定义为pre,意思是在请求前执行
  • filterOrder : 过滤器的执行顺序,当请求在一个阶段存在多个过滤器时,需要根据方法的返回值来判断过滤器的执行顺序
  • shouldFilter: 过滤器是否需要执行,这里直接返回true,因为该过滤器对所有的请求都生效
  • run: 过滤器的具体逻辑,这里我们通过rc.setResponseStatusCode(401)设置失效的标志,rc.setSendZuulResponse(false)令Zuul过滤该请求

在实现了自定义过滤器之后,它并不会直接生效,我们还需要为其创建具体的Bean才能启动该过滤器。

@EnableZuulProxy
@SpringBootApplication
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
@Bean
public AccessFilter filter(){
return new AccessFilter();
}
}

在对api-gateway服务完成了上面的改造之后,我们可以重新启动它,并发起下面的请求,对上面的过滤器做一个验证

  • 输入 http://localhost:5555/api-a/hello : 返回 401错误
  • 输入 http://localhost:5555/api-a/hello?accessToken=token,正确路由到server-provider的/hello 接口,并返回Hello World。

到这里,对于API网关的快速入门示例就搭建完成了,通过对Spring Cloud Zuul 网关的搭建,我们能认知到网关的重要性,可以总结如下:

  • 它作为系统的统一入口, 屏蔽了系统内部各个微服务的细节。
  • 它可以与服务治理框架结合,实现自动化的服务实例维护以及负载均衡的路由转发。
  • 它可以实现接口权限校验与微服务业务逻辑的解耦。
  • 通过服务网关中的过滤器, 在各生命周期中去校验请求的内容, 将原本在对外服务层做的校验前移, 保证了微服务的无状态性, 同时降低了微服务的测试难度, 让服务本身更集中关注业务逻辑的处理。


            </div>
目录
相关文章
|
Java 索引 Spring
spring data elasticsearch:从零搭建springboot整合spring data elasticsearch4.2.x环境
我们之间讲解了[springboot整合spring data elasticsearch3.x](https://blog.csdn.net/qq_24950043/article/details/125570709)。但elasticsearch的更新十分活跃,截止目前已经到了8.x版本。而spring data elasticsearch3.x所对应的es版本还是6.8.x 所以今天我们来讲解与spring-data-elasticsearch3.x有较大变化的spring data elasticsearch4.2.x版本如何整合到springboot
368 0
spring data elasticsearch:从零搭建springboot整合spring data elasticsearch4.2.x环境
|
XML Oracle Java
Spring boot——logback 基础使用篇(一)
Spring boot——logback 基础使用
Spring boot——logback 基础使用篇(一)
|
存储 消息中间件 SpringCloudAlibaba
手把手教你,从零开始搭建Spring Cloud Alibaba!这份笔记太牛了
Spring Cloud Alibaba 是阿里巴巴提供的微服务开发一站式解决方案,是阿里巴巴开源中间件与 Spring Cloud 体系的融合。 Springcloud 和 Srpingcloud Alibaba 区别? SpringCloud: 部分组件停止维护和更新,给开发带来不便;SpringCloud 部分环境搭建复杂, 没有完善的可视化界面,我们需要大量的二次开发和定制;SpringCloud 配置复杂,难以上手, 部分配置差别难以区分和合理应用 Srpingcloud Alibaba: 阿里使用过的组件经历了考验,性能强悍,设计合理,现在开源 出来成套的产品搭配完善的可视化
|
XML 消息中间件 开发框架
后端开发必须知道的Spring框架基础模块大全
后端开发必须知道的Spring框架基础模块大全
177 0
后端开发必须知道的Spring框架基础模块大全
|
设计模式 XML 存储
Spring 基础容器 BeanFactory
什么是BeanFactory? Spring官网对BeanFactory的解释 BeanFactory API 为Spring的IoC功能提供了底层基础。它的特定契约主要用于Spring的其他部分以及相关第三方框架其他部分的集成,它的DefaultListableBeanFactory实现是更高级别GenericApplicationContext容器的一个委托。
143 0
Spring 基础容器 BeanFactory
|
JSON 前端开发 Java
spring data elasticsearch:从零搭建springboot整合spring data elasticsearch3.x环境
spring-data系列为众多中间件、数据库的操作提供了极其方便的API,对于elasticsearch也不例外,spring-data-elasticsearch不仅为我们提供了现成的CRUD接口,也提供了简便的各类java client的整合方案 所以今天我们就来讲解,如果在springboot项目中整合spring-data-elasticsearch
532 0
spring data elasticsearch:从零搭建springboot整合spring data elasticsearch3.x环境
|
Java Shell 开发工具
Spring源码阅读 之 搭建源码阅读环境(IDEA)
Spring源码阅读 之 搭建源码阅读环境(IDEA)
125 0
Spring源码阅读 之 搭建源码阅读环境(IDEA)
|
关系型数据库 MySQL
《02Spring Boot2.6实战 - 搭建电商项目架构连接MySQL》电子版地址
02Spring Boot2.6实战 - 搭建电商项目架构连接MySQL.ppt
73 0
《02Spring Boot2.6实战 - 搭建电商项目架构连接MySQL》电子版地址
|
前端开发 Java 应用服务中间件
【Spring Cloud】新闻头条微服务项目:自媒体前后端搭建&素材管理(含优化)
主要介绍自媒体端的前后端搭建及素材管理中的上传图片素材、获取素材列表并展示、收藏素材、删除素材,最后对删除素材做了优化,优化了其逻辑的合理性。
318 0
【Spring Cloud】新闻头条微服务项目:自媒体前后端搭建&素材管理(含优化)
|
Java 关系型数据库 MySQL
Spring Boot + MySQL 快速搭建管理系统,不能太容易了。。(下)
Spring Boot + MySQL 快速搭建管理系统,不能太容易了。。(下)
Spring Boot + MySQL 快速搭建管理系统,不能太容易了。。(下)