目录

一、服务网关使用背景

二、服务网关的种类和区别

三、Gateway 简介

优点

缺点

Gateway组成

三、快速入门案例

1、创建网关服务,引入 GetAway 依赖

2、创建主类

3、配置GetAway网关服务

4、创建 订单服务模块 及创建 商品服务模块

5、进行测试

使用网关之前

使用网关之后


一、服务网关使用背景

        如下图所示,订单服务 调用 商品服务 的 流程的变化从1-4。

  • 1、最初:订单服务使用 restTemplate 调用商品服务,订单服务自己维护ip地址。
  • 2、然后:订单服务使用 restTemplate 调用商品服务,都使用注册中心。
  • 3、然后:订单服务使用 新的负载均衡 调用商品服务,使用ribbon。
  • 4、最后:订单服务使用 优雅的方式去 调用商品服务,使用openfeign。

        存在问题:此时,如果有新的客户端分别调用 订单服务 和 商品服务,那么这个客户端需要维护新的ip和port。而新客户端调用这些微服务时候,微服务又需要解决进行认证鉴权、安全校验、跨域问题。如果微服务 订单服务 和 商品服务 可能有成百上千个。此时存在的问题是: 

  • 新客户端要维护成千上万个ip和port,维护困难。
  • 新客户端需要进行认证鉴权、安全校验、跨域,那么认证鉴权、安全校验、跨域等问题比较复杂,若在每个微服务都进行解决认证鉴权、安全校验、跨域问题等问题,又会造成代码重复问题。

       解决方案:使用服务网关。而服务网关也是一个微服务,也可从注册中心获取ip。因此,所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等。添加上API网关之后,系统的架构图变成了如下所示:

二、服务网关的种类和区别

  • 服务网关种类
名称作用问题
Ngnix+lua使用nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用。lua是一种脚本语言,可以来编写一些简单的逻辑,nginx支持lua脚本。
Kong基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。

1、只支持Http协议

2、二次开发,自由扩展困难

3、提供管理API,缺乏更易用的管控、配置方式。

zuulNetflix开源的网关,功能丰富,使用JANA开发,易于二次开发zuul1.0 缺乏管控,无法动态配置;依颡组件较多;处理Http请求依赖的是Web容器,性能不如Nginx
Spring cloud GatewaySpring cloud Gateway Spring公司为了替换Zuul而开发的网关服务
  • zuul 和 Spring cloud Gateway 区别
zuulSpring cloud Gateway
Zuul 1.x,是基于阻塞V/O的API网关Spring Cloud Gateway基于异步非阻塞模型,建立在Spring Framework5、Project Reactor和Spring Boot 2之上
Zuul 1.x,是基于Servlet 2.5,使用阻塞架构和传统的IO处理模型(高并发线程数量上涨,严重影响请求处理时间),它不支持任何长连接(如WebSocket)Spring Cloud Gateway基于webplus(webplus基于Servlet 3.x异步非阻塞的模型,核心是Reactor响应式编程),支持WebSocket

Zuul 1.x,设计模式和Nginx较像,每次V/О操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成。

但是差别是Nginx用C++实现,Zuul用Java实现,故而JVM本身会有第—次加载较慢的情况,使得Zuul的性能相对较差。

根据官方提供的基准测试,Spring Cloud Gateway的RPS(每秒请求数)是Zuul的1.6倍。
Zuul 2.x  理念更先进,想基于Netty非阻塞和支持长连接,性能较Zuul 1.x有较大提升,但SpringCloud目前还没有整合。与Spring紧密集成拥有更好的开发体验

三、Gateway 简介

       Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。它的目标是替代 Netilx Zuul,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标和限流。

优点

  • 性能强劲:是第一代网关Zuul的1.6倍
  • 功能强大:内置了很多实用的功能,例救转发、监控、限流等
  • 设计优雅,容易扩展

缺点

  • 其实现依赖Netty与WebFlux,不是传统的Servlet编程模型,学习成本高
  • 不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行
  • 需要Spring Boot 2.0及以上的版本,才支持

Gateway组成

       Gateway核心是:route、Predicate、Filter。

  • route:动态路由,能够匹配任何请求属性;
  • Predicate:断言,如果请求与断言相匹配则进行路由;
  • Filter:过滤器,可以在请求被路由前或者之后对请求进行修改。

三、快速入门案例

     流程:通过网关服务,对访问路径进行请求,如果请求符合断言,则调用订单服务模块。

1、创建网关服务,引入 GetAway 依赖

完整pom如下:

<?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">
  <parent>
    <artifactId>springcloudbase</artifactId>
    <groupId>com.hwadee.springcloud2022</groupId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.hwadee.springcloud</groupId>
  <artifactId>routeServer7000</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <dependencies>
    <!--
      引入GetAway依赖
      注意引GetAway网关服务模块中不能引入 starter-web,
      如果引入 starter-web 会引入tomcat,与 GetAway 底层的webFlux冲突。
    -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <!--Eureka Client-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
  </dependencies>

</project>

注意:如果GetAway网关服务模块中引入GetAway依赖,则不能引入 starter-web, 如果引入 starter-web 则会引入tomcat,与 GetAway 底层的webFlux冲突,如下图:

2、创建主类

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

@SpringBootApplication
@EnableEurekaClient// 启动 eureka 客户端
public class RouteServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RouteServerApplication.class, args);
    }
}

3、配置GetAway网关服务

       添加配置文件,配置文件中配置GetAway网关服务。

server:
  port: 7000
spring:
  application:
    name: route-service # 为当前商品服务命名
  cloud:
    gateway:
      routes:                         #路由数组[路由就是指定当请求满足什么条件的时候转到哪个微服务〕
        - id: order_route             #当前路由的标识,要求唯一,默认uuid。自定义命名:一般是 要转发的服务名_route
          uri: http://localhost:9000  #请求最终要被转发到的 订单服务 的地址
          order: 1                    #路由的优先级,数字越小级别越高
          predicates:                 #断言(条件判断返回值类型是boolean,就是路由转发要满足的条件)
            - Path=/order-serv/**     #当请求路径满足Path指定的规则时,才进行路由转发,如/order-serv/order/select/22
          filters:                    #过滤器,请求在传递过程中可以通过过滤器对请求进行一定的修改
            - StripPrefix=1           #使用 StripPrefix= x 是指在转发前去掉几层路径。见下面属性说明案例。
         
          
        - id: order_route             #当前路由的标识,要求唯一
          uri: http://localhost:9001  #请求最终要被转发到的 产品服务 的地址
          order: 1                    #路由的优先级,数字越小级别越高
          predicates:                 #断言(条件判断返回值类型是boolean,就是路由转发要满足的条件)
            - Path=/product-serv/**   #当请求路径满足Path指定的规则时,才进行路由转发
          filters:                    #过滤器,请求在传递过程中可以通过过滤器对请求进行一定的修改
            - StripPrefix=1           #使用 StripPrefix= 1 在转发前去掉1层路径。
         

eureka:
  client:
    service-url: # 配置服务注册地址,与 eureka-server 中暴露地址保持一致
      defaultZone: http://localhost:8000/eureka
  instance:
    prefer-ip-address: true  # 是否使用 IP 地址注册,默认 false
    # instance-id: product-service  # 实例 id,服务的唯一标识
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # 如果想在控制页面看到服务地址与端口,可以将 instance-id 这样配置
    lease-renewal-interval-in-seconds: 5  # 发送心跳的间隔,单位秒,默认 30
    lease-expiration-duration-in-seconds: 10 # 续约到期时间,单位秒,默认90

    属性介绍说明:

routes#路由数组[路由就是指定当请求满足什么条件的时候转到哪个微服务]
id

id: order_route  # 表示当前路由的标识,要求唯一,默认是uuid。

一般是转发到哪个服务就在哪个服务名后加route

uri#请求最终要被转发到的哪个服务的地址,如:http://localhost:8081
orderorder:1 #路由的优先级,数字越小级别越高
predicates#断言(条件判断返回值类型是boolean,就是路由转发要满足的条件)
Path# - Path=/product-serv/**,当请求路径满足Path指定的规则时,才进行路由转发,如:/product-serv/order/buy/22 满足条件,会进行路由转发。
filters#过滤器,请求在传递过程中可以通过过滤器对请求进行一定的修改
StripPrefix  

# -StripPrefix = 1 是指转发之前去掉1层路径,例如:

我们的需求是 当浏览器访问路径是http://localhost:8081/order-serv/order/buy/22时候,
希望访问微服务的真是路径是:http://localhost:8081/order/buy/22,
但是如果不使用过滤器,访问order服务的路径会变为变为:http://localhost:8081/order-serv/order/buy/22,
与我们实际希望访问的路径http://localhost:8081/order/buy/22不符,访问不到实际的真实路径。
使用StripPrefix=1是指在转发前去掉几层路径。如果StripPrefix=1,则将请求
那么访问http://localhost:8081/order-serv/order/buy/22时候,会去掉第一层“/order-serv”,
此时路径变为我们希望的真实路径http://localhost:8081/order/buy/22 

4、创建 订单服务模块 及创建 商品服务模块

     创建 订单服务模块 及创建 商品服务模块 见第十章 

     删除 商品服务 模块中的 controller 内 selectHystrixBreaker方法里的超时代码,修改如下:

import com.hwadee.springcloud.entity.Product;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;

@RestController
@RequestMapping("/product")
public class ProductController {
    //方便后面讲负载均衡,查看ip,此处获取配置中的端口号和ip
    @Value("${server.port}")
    private String port;
    @Value("${spring.cloud.client.ip-address}")
    private String ip;

    @RequestMapping(value = "/select/{id}")
    @HystrixCommand(fallbackMethod = "selectHystrixBreakerFallback", commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),//请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//时间窗口期
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),//失败率达到多少后跳闸
    })
    public Product selectHystrixBreaker(@PathVariable Long id) {
        // 程序异常 测试异常熔断
        if (id<=0) {
            throw new RuntimeException("id无效");
        }

        // 程序正常执行
        Product product = new Product();
        product.setId(id);
        // 后面需要测试负载均衡,所以返回 ip 地址及端口号
        product.setName("当前访问服务地址:" + ip + ":" + port + "  " + "从购物车删除订单,订单号:" + id);
        product.setPrice(new BigDecimal(10000.0));
        System.out.println(product);

        return product;
    }

    public Product selectHystrixBreakerFallback(Long id) {
        Product product = new Product();
        product.setId(id);
        product.setName("当前访问服务地址:" + ip + ":" + port + "  " + "查询订单异常,通过注解 @HystrixCommand()指定的备选方案进行服务熔断");
        product.setPrice(new BigDecimal(10000.0));
        return product;
    }
}

5、进行测试

     分别启动 注册中心、订单服务、商品服务、和网关服务

     分别访问以下地址查看使用网关之前和使用网关之后的效果。

  • 使用网关之前

       访问商品服务:http://localhost:9001/product/select/2
       访问订单服务:http://localhost:9000/order/select/22

  • 使用网关之后

      访问商品服务:http://localhost:7000/product-serv/product/select/22
      访问订单服务:http://localhost:7000/order-serv/order/select/22

第十章:Hystrix断路器详解+服务熔断

第十二章:Spring Cloud Config 统一配置中心详解

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐