• SpringCloud微服务实战系列(十二)API网关之Gateway使用
    date_range 2019-09-09 10:09:01
    folder SpringCloud专题
    person 陈付菲 公开
    thumb_up 点赞3
    remove_red_eye 围观1692

    SpringCloud微服务实战系列(十二)API网关之Gateway使用

    一、概述

    API网关是一个更为智能的应用服务器,它的定义类似于面向对象设计模式中的Facade模式,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤。它除了要实现请求路由、 负载均衡、 校验过滤等功能之外,还需要更多能力,比如与服务治理框架的结合、请求转发时的熔断机制、服务的聚合等一系列高级功能。

    在SpringCloud全家桶中,API网关有Zuul和gateway两种实现。

    1. zuul进来逐渐被gateway取代。
    2. zuul可以整合进任何SpringBoot应用
    3. gateway基于异步非阻塞模型,使用的netty+webflux实现,不能加入web依赖,默认加载加入webflux依赖。
    4. Spring Cloud zuul使用的仍是zuul 1.x,zuul 2.x也是异步非阻塞的。
    5. 选用zuul还是gateway,要看使用场景,如果在已有的非WebFlux项目使用,要用zuul。单纯的API网关独立部署,建议gateway。

    代码可以在SpringBoot组件化构建https://www.pomit.cn/java/spring/springcloud.html中的EurekaGateway、ZkGateway和ConsulGateway组件中查看,并下载。分别对应三种不同的服务注册方式。

    如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以 加入我们的java学习圈,点击即可加入 ,共同学习,节约学习时间,减少很多在学习中遇到的难题。

    二、项目配置

    本篇的配置基于服务注册发现的项目基础上,并选择Consul + Gateway为例讲解如何使用zuul做API网关。至于(Eureka + Gateway)和 (zookeeper + Gateway)写法上和(consul + Gateway)是一样的,不再分开章节描述。

    这里选择Consul + Gateway为例,是因为它需要一个额外的健康检查接口,其他无区别。

    可以参考我前面的文章做对比:

    《SpringCloud技术指南系列(五)服务注册发现之Consul服务调用》

    《SpringCloud技术指南系列(三)服务注册发现之Eureka服务调用》

    《SpringCloud技术指南系列(七)服务注册发现之Zookeeper服务调用》

    2.1 引入依赖

    需要在建立好的项目中引入spring-cloud-starter-gateway,不能引入spring-boot-starter-web,因为它用的是webflux。

    如果需要使用hystrix,要显式引入spring-cloud-starter-netflix-hystrix。

    本篇是在consul的服务发现基础上进行gateway的配置,因此还需要引入consul相关配置。

    依赖如下:

    <?xml version="1.0"?>
    <project
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
        xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>cn.pomit</groupId>
            <artifactId>springcloudwork</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>
        <artifactId>ConsulGateway</artifactId>
        <name>ConsulGateway</name>
        <url>http://maven.apache.org</url>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
            <maven-jar-plugin.version>2.6</maven-jar-plugin.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
        </dependencies>
    </project>
    

    父模块pom文件可以在https://www.pomit.cn/spring/SpringCloudWork/pom.xml获取。

    2.2 配置文件

    这里使用yaml文件写配置,配置文件application.yml:

    application.yml:

    server:
       port: 8819
    spring:
       application:
          name: consulGateway
       cloud:
          consul:
             host: 127.0.0.1
             port: 8500
             discovery:
                prefer-ip-address: true
                healthCheckPath: /consul/health
          gateway:
             discovery:
                locator:
                   enabled: true
                   lowerCaseServiceId: true
             routes:
             -  id: myRoute
                uri: lb://consulFeign
                predicates:
                - Path=/test/**
                filters:
                -  StripPrefix=1
                -  name: Hystrix
                   args:
                      name: fallbackcmd
                      fallbackUri: forward:/incaseoffailureusethiss
             -  id: myOutsideRoute
                uri: https://cn.bing.com/
                predicates:
                - Path=/search/**
                filters:
                -  StripPrefix=1

    这里,表示API网关在8819端口监听,名字是consulGateway。

    • spring.cloud.consul开头的配置时consul服务注册发现的配置。前面章节已经有说明。
    • spring.cloud.gateway.discovery.locator.enabled开启路由转发功能
    • spring.cloud.gateway.discovery.locator.lowerCaseServiceId是指注册到cunsul的服务使用小写。
    • spring.cloud.gateway.routes.*:配置路由转发。id是唯一的,uri如果使用lb:开头,表示使用注册的服务进行负载均衡。predicates.Path:表示请求的路径。filters.StripPrefix表示转发时路径去掉几层前缀。filters.name如果是Hystrix,表示使用Hystrix进行熔断降级,需要配置fallbackUri进行降低转发。

    三、服务不可用降级接口

    如果配置了hystrix做熔断。当请求次数失败次数过多(我测试第一次是8次,第二次变7次了),将不再请求,直接请求/incaseoffailureusethiss降级。

    在这里插入图片描述

    如图所示,1.01s是请求失败的过程,7次之后,就直接熔断,所以响应时间小于10ms了。

    FallbackController:

    package cn.pomit.springbootwork.consulgateway.web;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import cn.pomit.springbootwork.consulgateway.model.ResultModel;
    
    @RestController
    public class FallbackController {
    
        @GetMapping("/incaseoffailureusethiss")
        public ResultModel fallback() {
            ResultModel rm = ResultModel.error("服务咱不可用,请稍后重试!");
    
            return rm;
        }
    }
    

    四、启动

    启动类无需做额外的配置,这里只有一个服务注册的注解@EnableDiscoveryClient。

    ConsulGatewayApplication :

    package cn.pomit.springbootwork.consulgateway;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @EnableDiscoveryClient
    @SpringBootApplication
    public class ConsulGatewayApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsulGatewayApplication.class, args);
        }
    
        @Bean
        @LoadBalanced
        RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
    

    五、其他web

    我们仍可以写自己的web服务,和Gateway的转发互不影响,但是路径别重复了。 ConsulGatewayRest :

    package cn.pomit.springbootwork.consulgateway.web;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    import cn.pomit.springbootwork.consulgateway.model.ResultModel;
    
    @RestController
    @RequestMapping("/consulgateway")
    public class ConsulGatewayRest {
    
        @RequestMapping(value = "/test", method = { RequestMethod.GET })
        public ResultModel ip() {
            return ResultModel.ok("非路由测试");
        }
    }
    

    六、Consul的健康检测

    这个是使用consul做注册中心才需要的。

    HealthWeb:

    package cn.pomit.springbootwork.consulgateway.web;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/consul")
    public class HealthWeb {
    
        @RequestMapping(value = "health", method = { RequestMethod.GET })
        public String health() {
            return "check health";
        }
    
    }
    

    七、使用到的实体

    ResultModel:

    package cn.pomit.springbootwork.consulgateway.model;
    
    /**
     * @author cff
     */
    public class ResultModel {
    
        private String errorCode;
        private String message;
        private Object data;
    
        public ResultModel() {
    
        }
    
        public ResultModel(String errorCode, String message) {
            this.errorCode = errorCode;
            this.message = message;
        }
    
        public ResultModel(String errorCode, String message, Object data) {
            this.errorCode = errorCode;
            this.message = message;
            this.data = data;
        }
    
        public String geterrorCode() {
            return errorCode;
        }
    
        public void seterrorCode(String errorCode) {
            this.errorCode = errorCode;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
    
        public static ResultModel ok() {
            return new ResultModel("0000","成功");
        }
    
        public static ResultModel ok(Object data) {
            return new ResultModel("0000","成功", data);
        }
    
        public static ResultModel error() {
            return new ResultModel("1111","失败");
        }
    
        public static ResultModel error(String msg) {
            return new ResultModel("1111","失败", msg);
        }
    
        public static ResultModel error(String msg, Object data) {
            return new ResultModel("1111", msg, data);
        }
    }
    
评论列表
  • nthan : 请问下,博客中的代码,有放在 github 上么? 5年前 回复 隐藏回复
    • feiyang 回复 nthan : https://www.pomit.cn/java/spring/springcloud.html这个地方可以下载,https://gitee.com/ffch/SpringCloudWork也可以 5年前 回复
mode_edit