热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

springcloud入门系列六:使用Zuul实现API网关服务

通过前面几次的分享,我们了解了微服务架构的几个核心设施,通过这些组件我们可以搭建简单的微服务架构系统。比如通过SpringCloudEureka搭建高可用的服务注册中心并实现

 

通过前面几次的分享,我们了解了微服务架构的几个核心设施,通过这些组件我们可以搭建简单的微服务架构系统。比如通过Spring Cloud Eureka搭建高可用的服务注册中心并实现服务的注册和发现;

通过Spring Cloud Ribbon或Feign进行负载均衡;通过Spring Cloud Hystrix进行服务容错保护以避免故障蔓延。微服务搭建好了之后我们肯定会提供给外部系统一些统一的RESTFul API服务接口进行调用,

但是当外部系统调用我们的RESTful API的时候,怎么确定它需要的功能具体是哪个服务提供的呢?这个就涉及到一个路由规则和服务实例列表的维护问题。

这就引入了我们今天的主角--Spring Cloud Zuul,它是基于Netflix Zuul实现的API网关组件。它可以解决两个大问题:

  1. 就是我们上面提到的路由规则和服务实例的维护问题
  2. 对于一些校验(比如登录校验等)冗余问题。 按照我们的习惯的做法,是在每个服务中都需要加入这些校验,但是这样会导致代码冗余并且维护也比较麻烦,有了Spring Cloud Zuul这个网关服务之后,我们可以将这些共通的校验放到网关里面统一维护。

好,接下来我们就来看下怎么实现这个网关服务。

一、构建网关,配置路由

 这里我们还是需要使用到前面的hello-service和feign-consumer服务。我们之前把feign-consumer作为服务消费者,但是别忘了在eureka体系里面,每个服务既是服务提供者又是服务消费者,所以feign-consumer也是一个服务提供者,并且http://localhost:9001/feign-consumer等接口就是它提供的服务。

接下来我们构建一个网关服务,代码结构如下:

  

代码实现步骤:

  1. 新建maven工程api-gateway
  2. 修改POM文件
    <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.0modelVersion>
      <groupId>com.samgroupId>
      <artifactId>api-gatewayartifactId>
      <version>0.0.1-SNAPSHOTversion>
      
      <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>1.5.1.RELEASEversion>
        parent>
    
        <properties>
            <javaVersion>1.8javaVersion>
        properties>
        
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloudgroupId>
                    <artifactId>spring-cloud-dependenciesartifactId>
                    <version>Camden.SR6version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
            dependencies>
    
        dependencyManagement>
    
        <dependencies>
            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-zuulartifactId>
            dependency>
    
        dependencies>
        
    project>
  3. 新建启动类
    /**
     * @EnableZuulProxy 开启Zuul 的API网关服务功能
     *
     */
    @EnableZuulProxy
    @SpringCloudApplication
    public class GateWayApp {
    
        public static void main(String[] args) {
            SpringApplication.run(GateWayApp.class, args);
        }
    }
  4. 新建application.properties
    server.port=5555
    spring.application.name=api-gateway
    
    #增加路由规则的配置
    #通过zuul.routes..path和zuul.routes..url进行配置,为路由的名字,可以任意指定,但是一组path和url的路由名要相同
    #如下面的例子:所有满足/api-a/** 规则的访问都会被路由转发到//localhost:9001的地址
    #也就是说,我们访问http://localhost:5555/api-a/hello的时候,API网关服务就会将该请#求路由到 http://localhost:9001/hello提供的微服务接口上
    zuul.routes.api-a.path=/api-a/**
    zuul.routes.api-a.url=http://localhost:9001
    
    
    zuul.routes.api-b.path=/api-b/**
    zuul.routes.api-b.url=http://localhost:9090

     

  5. 测试,启动eureka、hello-service、feign-consumer以及本次新加的api-gateway服务,然后访问http://localhost:5555/api-a/feign-consumer

    成功访问到了feign-consumer的服务接口--feign-consonsumer。

以上步骤实现了传统路由的配置,这种配置有个大的缺点,就是需要手工在application.properties文件中进行路由规则的配置,当服务很多的时候,维护工作量就会很大。为了减小维护成本,还有另外一种路由--面向服务的路由。

二、面向服务的路由

Spring Cloud Zuul和Eureka进行整合,我们可以让路由的path不是映射具体的url,而是具体的某个服务,而服务的url则交给Eureka服务发现机制自动维护,这类路由就是面向服务的路由。具体代码配置如下:

  1. 修改POM文件,引入Eureka依赖
    
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-eurekaartifactId>
            dependency>
  2. 修改application.properties配置文件
    server.port=5555
    spring.application.name=api-gateway
    
    zuul.routes.api-a.path=/api-a/**
    
    #这里用serviceId代替url,用服务名代替ip+端口号
    zuul.routes.api-a.serviceId=hello-service

    eureka.client.service-url.defaultZOne=http://localhost:1111/eureka

    注意:zuul.routes.api-a.url=hello-service也能实现功能,但是它不能进行正常的负载均衡和容错保护。

  3. 测试,访问http://localhost:5555/api-a/hello

    访问成功。

 三、服务路由的默认规则

在面向服务的路由中,由于名字是随意起的,那么是不是可以这样:

zuul.routes.hello-service.path=/hello-service/**
zuul.routes.hello-service.serviceId=hello-service

名字就是服务名,其实在实际的应用中,我们往往就是这样命名的。如果有这样的规则的话,那Zuul就可以帮我们默认实现这样的功能,进一步省去了配置的麻烦。

我们来做个实验,将配置文件改为:

server.port=5555
spring.application.name=api-gateway

eureka.client.service-url.defaultZOne=http://localhost:1111/eureka

然后页面访问验证

访问成功。

 

但是由于默认情况下,Eureka上的服务都会被Zuul创建默认的映射关系来进行路由,使得我们不想对外开放的服务也被外部访问到,这个时候可以通过配置zuul.ignored-services来进行配置不需要自动创建路由的规则。当zuul.ignored-services=*的时候,所有的服务都不会自动创建路由规则,这个时候需要通过前面的配置进行相关路由配置了。

================华丽的分割线===================

前面说了那么多都是围绕一个问题展开的:路由规则和服务实例的维护问题,那么怎么解决第二个问题(校验冗余问题)呢?

 四、请求过滤

为了在API网关中实现对客户端请求的校验,我们可以通过过滤器来实现对请求的拦截和过滤,实现方法比较简单,只需要继承ZuulFilter抽象类并实现其四个方法就行了。

修改api-gateway:

  1. 新增过滤器类
    /**
     * 继承ZuulFilter,并且实现其4个接口
     *
     * 用来进行请求过滤
     *
     */
    public class AccessFilter extends ZuulFilter {
        Logger logger = LoggerFactory.getLogger(AccessFilter.class);
        /* 
         * shouldFilter 判断该过滤器是否需要被执行
         * 
         * 这里直接返回true,表示该过滤器对所有请求都会生效。
         * 实际运用中我们可以利用该函数指定过滤器的有效范围
         */
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        /*
         * 过滤器的具体逻辑
         * 
         * 这里我们通过ctx.setSendZuulResponse(false)让zuul过来请求,不对其进行路由
         * 然后通过ctx.setResponseStatusCode(401)设置了返回的错误码
         * 
         */
        @Override
        public Object run() {
            RequestContext context = RequestContext.getCurrentContext();
            HttpServletRequest request = context.getRequest();
            Object accessToken = request.getParameter("accessToken");
            
            logger.info("send {} request to {}", request.getMethod(),request.getRequestURL().toString());
            if(accessToken == null) {
                context.setSendZuulResponse(false);
                context.setResponseStatusCode(401);
            }
            
            return null;
        }
    
        /* filterType 返回过滤器类型
         * 他决定了过滤器在请求的哪个生命周期中执行。这里定义为pre,代表会在请求被路由前执行。
         * 
         * pre:请求执行之前filter 
         * route: 处理请求,进行路由 
         * post: 请求处理完成后执行的filter 
         * error:出现错误时执行的filter
         */
        @Override
        public String filterType() {
            return "pre";
        }
        
        
        /* 
         * filterOrder 返回过滤器的执行顺序
         * 
         * 当请求在一个阶段有多个过滤器是,需要根据该方法的返回值来依次执行
         * 
         */
        @Override
        public int filterOrder() {
            return 0;
        }
    
    }
  2. 修改启动类
    /**
     * @EnableZuulProxy 开启Zuul 的API网关服务功能
     *
     */
    @EnableZuulProxy
    @SpringCloudApplication
    public class GateWayApp {
    
        //追加bean的是实现
        @Bean
        public AccessFilter accessFilter() {
            return new AccessFilter();
        }
        
        public static void main(String[] args) {
            SpringApplication.run(GateWayApp.class, args);
        }
    }
  3. 测试
    1. )访问http://localhost:5555/hello-service/hello,访问失败
      1.  

    2. )访问http://localhost:5555/hello-service/hello?accessToken=token,正常访问

       

 

修改后的代码结构:

 

 五、拓展延伸

其实路由功能在真正运行时,他的路由映射和请求转发都是由几个不同的过滤器完成的。

路由映射主要通过pre类型的过滤器完成,他将请求路径与配置的路由规则进行匹配,找到需要转发的目标地址。

而请求转发的部分则是由route类型的过滤器完成的,对pre类型过滤器获取的路由地址进行转发。

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

 


推荐阅读
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • Spring Cloud Config 使用 Vault 作为配置存储
    本文探讨了如何在Spring Cloud Config中集成HashiCorp Vault作为配置存储解决方案,基于Spring Cloud Hoxton.RELEASE及Spring Boot 2.2.1.RELEASE版本。文章还提供了详细的配置示例和实践建议。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
  • 本文详细介绍了Java中org.w3c.dom.Text类的splitText()方法,通过多个代码示例展示了其实际应用。该方法用于将文本节点在指定位置拆分为两个节点,并保持在文档树中。 ... [详细]
  • 使用Vultr云服务器和Namesilo域名搭建个人网站
    本文详细介绍了如何通过Vultr云服务器和Namesilo域名搭建一个功能齐全的个人网站,包括购买、配置服务器以及绑定域名的具体步骤。文章还提供了详细的命令行操作指南,帮助读者顺利完成建站过程。 ... [详细]
  • 该平台旨在为大型企业提供一个高效、灵活且可扩展的分布式微服务架构解决方案。它采用模块化、微服务化和热部署的设计理念,结合当前最先进且无商业限制的主流开源技术,如Spring Cloud、Spring Boot2、MyBatis、OAuth2和Element UI,实现前后端分离的系统管理平台。 ... [详细]
  • 解决JAX-WS动态客户端工厂弃用问题并迁移到XFire
    在处理Java项目中的JAR包冲突时,我们遇到了JaxWsDynamicClientFactory被弃用的问题,并成功将其迁移到org.codehaus.xfire.client。本文详细介绍了这一过程及解决方案。 ... [详细]
  • 本文详细探讨了在微服务架构中,使用Feign进行远程调用时出现的请求头丢失问题,并提供了具体的解决方案。重点讨论了单线程和异步调用两种场景下的处理方法。 ... [详细]
  • Spring Cloud学习指南:深入理解微服务架构
    本文介绍了微服务架构的基本概念及其在Spring Cloud中的实现。讨论了微服务架构的主要优势,如简化开发和维护、快速启动、灵活的技术栈选择以及按需扩展的能力。同时,也探讨了微服务架构面临的挑战,包括较高的运维要求、分布式系统的复杂性、接口调整的成本等问题。最后,文章提出了实施微服务时应遵循的设计原则。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • This guide provides a comprehensive step-by-step approach to successfully installing the MongoDB PHP driver on XAMPP for macOS, ensuring a smooth and efficient setup process. ... [详细]
  • ServiceStack与Swagger的无缝集成指南
    本文详细介绍了如何在ServiceStack项目中集成Swagger,以实现API文档的自动生成和在线测试。通过本指南,您将了解从配置到部署的完整流程,并掌握如何优化API接口的开发和维护。 ... [详细]
author-avatar
--Fac_k-
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有