文章目录
- 网关介绍
- [Spring Cloud Gateway](#Spring Cloud Gateway)
网关介绍
问题
前面我们通过 Eureka,Nacos 解决了服务注册,服务发现的问题,使用 Spring Cloud LoadBalance 解决了负载均衡的问题,使用 OpenFeign 解决了远程调用的问题
但是当前所有微服务的接口都是直接对外暴露的,可以直接通过外部访问。为了保证对外服务的安全性,服务端实现的微服务接口通常都带有一定的权限校验机制。由于使用了微服务,原本一个应用的多个模块拆分成了多个应用,我们不得不实现多次校验逻辑。当这套逻辑需要修改时,我们需要修改多个应用,加重了开发人员的负担
针对以上问题,一个常用的解决方案是使用 API 网关
比如企业管理
- 外部人员去公司办理业务,公司需要先核实对方的身份再去进行办理
- 最开始只有一个员工,这个员工核实之后直接办理即可(单体架构)
- 随着公司的发展,划分了多个部门,每个部门负责的事情不同,每个部门需要先核实对方的身份再进行办理(微服务架构)
这个流程存在一些问题
- 办事效率低
- 增加了员工的工作流程
我们对此进行改进,设立前台,统一由前台来进行身份的校验。前台身份校验通过后,其他部门就设置信任,直接办理
- 前台就类似
API网关
什么是 API 网关
API 网关(简称网关)也是一个服务,通常是后端服务的唯一入口。它的定义类似设计模式中的 Facade 模式(门面模式,也称外观模式),它就类似整个微服务架构的门面,所有的外部客户端访问,都需要经过它来进行调度和过渡
网关核心功能:
- 权限控制:作为微服务的入口,对用户进行权限校验,如果校验失败则进行拦截
- 动态路由:一切请求先经过网关,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务
- 负载均衡:当路由的目标服务有多个时,还需要做负载均衡
- 限流:请求流量过高时,按照网关中配置微服务能够接受的流量进行放行,避免服务压力过大
类似前台的工作
- 权限控制:身份验证
- 动态路由:根据外来客户的需求,把客户带到指定的部门去处理
- 负载均衡:一个部门有很多人时,前台会帮客户选择具体某个人处理
- 限流:公司到访客户较多时,进行流量限制,比如告知明天再来
常见网关实现
业界常用的网管方式有很多,技术方案也较成熟,其中不乏很多开源产品,比如 Nginx、Kong、Zuul Spring Cloud Gateway 等
Zuul
Zuul 是 NetFlix 公司开源的一个 API 网关组件,是 Spring Cloud Netflix 子项目的核心组件之一,它可以和 Eureka、Ribbon、Hystrix 等组件配合使用
Spring Cloud Gateway
其是 Spring Cloud 的一个全新的 API 网关项目,基于 Spring + SpringBoot 等技术开发,目的是为了替换掉 Zuul。旨在为微服务架构提供一种简单而有效的途径来转发请求,并为他们提供横切关注点,比如:安全性,监控/指标和弹性
Spring Cloud Gateway
快速上手
我们通过以下的演示,先来了解网关的基本功能
常见网关项目
API 网关也是一个服务

引入网关依赖
xml
<!-- 网络关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 基于nacos实现服务发现依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
添加路由配置
创建 application.yml 文件,添加如下配置:
yml
server:
port: 8848 # 网关端口
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes: # 网关路由配置
- id: product-service # 路由ID,自定义,唯一即可
uri: lb://product-service # 目标服务地址
predicates: # 路由条件
- Path=/product/**
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
配置字段说明:
id:自定义路由ID,保持唯一url:目标服务机制,支持普通URI及lb://应用注册服务名称,lb表示负载均衡,使用lb://方式表示从注册中心获取服务地址predicates:路由条件,根据匹配结果决定是否执行该请求路由,上述代码中,我们把符合Path规则的一切请求,都代理到uri参数指定的地址
测试
启动 API 网关服务
- 通过网关服务访问
product-service
http://127.0.0.1:8858/product/1001

url符合yml文件中配置的/product/**规则,路由转发到product-service: http://product-service/product/1001
- 通过网关服务访问
order-service
http://127.0.0.1:8858/order/1

url符合yml文件中配置的/ordre/**规则,路由转发到product-service: http://order-service/product/1001
Route Predicate Factories
Predicate
Predicate 是 Java 8 提供的一个函数式编程接口,它接收一个参数并返回一个布尔值,用于条件过滤,请求参数的校验
java
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
//...
}
代码演示
- 定义一个
Predicate
java
public class StringPredicate implements Predicate<String> {
@Override
public boolean test(String s) {
return s.isEmpty();
}
}
- 使用这个
Predicate
java
public class StringPredicateTest {
public static void main(String[] args) {
Predicate<String> predicate = new StringPredicate();
System.out.println(predicate.test(""));
System.out.println(predicate.test("hahahaha"));
}
}
- 运行结果

Predicate 的其他写法
- 内置函数
java
public class StringPredicateTest {
public static void main(String[] args) {
Predicate<String> predicate = new Predicate<String>(){
@Override
public boolean test(String s) {
return s.isEmpty();
}
};
System.out.println(predicate.test(""));
System.out.println(predicate.test("hahahaha"));
}
}
lambda写法
java
class StringPredicateTest {
public static void main(String[] args) {
Predicate<String> predicate = s -> s.isEmpty();
System.out.println(predicate.test(""));
System.out.println(predicate.test("hahahaha"));
}
}
Predicate<String> predicate = s -> s.isEmpty();也可以写成Predicate<String> isEmpty = String::isEmpty;
isEqual (ObjecttargetRef):⽐较两个对象是否相等, 参数可以为Nulland (Predicateother):短路与操作, 返回⼀个组成Predicateor (Predicateother): 短路或操作, 返回⼀个组成Predicatetest (Tt): 传⼊⼀个Predicate参数,⽤来做判断negate():返回表⽰此Predicate逻辑否定的Predicate
