概述
API网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求。如果让客户端直接与各个微服务通信,会有以下的问题:
- 客户端会多次请求不同的微服务,增加了客户端的复杂性。
- 存在跨域请求,在一定场景下处理相对复杂。
- 认证复杂,每个服务都需要独立认证。
- 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施。
- 某些微服务可能使用了防火墙 / 浏览器不友好的协议,直接访问会有一定的困难。
以上这些问题可以借助API网关解决。API网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过 API 网关这一层。也就是说,API 的实现方面更多的考虑业务逻辑,而安全、性能、监控可以交由API网关来做,这样既提高业务灵活性又不缺安全性。
由于篇幅比较长, 决定分几篇来介绍相关知识:
- spring-cloud-gateway简介+基本环境搭建
- spring-cloud-gateway过滤器+审计
- spring-cloud-gateway静态路由
- spring-cloud-gateway动态路由
- spring-cloud-gateway限流+过载保护
- spring-cloud-gateway认证
- spring-cloud-gateway鉴权
环境参数
- 开发工具: IDEA
- 基础工具: Maven+JDK8
- SpringBoot版本: 2.1.4.RELEASE
- SpringCloud版本: Greenwich.SR3
- 服务发现: consul
consul搭建可以参照官网,开箱即用。也可以参照Docker实战之Consul集群 基于docker环境搭建。
SpringCloud项目搭建
项目pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
| <?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>
<groupId>cn.idea360</groupId> <artifactId>spring-cloud-learning</artifactId> <version>1.0</version> <modules> <module>idc-provider1</module> <module>idc-provider2</module> <module>idc-gateway</module> </modules>
<packaging>pom</packaging> <name>spring-cloud-learning</name>
<licenses> <license> <name>Apache License, Version 2.0</name> <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url> <distribution>repo</distribution> </license> </licenses>
<developers> <developer> <name>cuishiying</name> <email>cuishiying163@163.com</email> </developer> </developers>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <java.version>1.8</java.version>
<spring-boot.version>2.1.4.RELEASE</spring-boot.version> <spring-cloud.version>Greenwich.SR3</spring-cloud.version> <docker.image.prefix>csy</docker.image.prefix> </properties>
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.51</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR3</version> <type>pom</type> <scope>import</scope> </dependency>
</dependencies> </dependencyManagement>
<build> <finalName>${project.name}</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin>
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.19.1</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> </plugins> </build>
<repositories> <repository> <id>aliyun</id> <name>aliyun</name> <url>https://maven.aliyun.com/repository/public</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://maven.aliyun.com/repository/spring</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-plugin</id> <name>spring-plugin</name> <url>https://maven.aliyun.com/repository/spring-plugin</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories>
</project>
|
服务提供者1(服务提供+服务消费)
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <?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>spring-cloud-learning</artifactId> <groupId>cn.idea360</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>idc-provider1</artifactId>
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> </plugin> </plugins> </build> </project>
|
配置文件application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| spring: application: name: idc-provider1 cloud: consul: host: localhost port: 8500 server: port: 2001
feign: client: config: remote-service: connectTimeout: 1000 readTimeout: 3000
|
SpringBoot项目入口Provider1App.java
1 2 3 4 5 6 7 8 9 10
| @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class Provider1App {
public static void main(String[] args) { SpringApplication.run(Provider1App.class, args); } }
|
远程调用RemoteService.java
1 2 3 4 5 6 7 8 9 10 11
| @FeignClient("idc-provider2") public interface RemoteService {
@GetMapping("/provider2") Object getProvider2();
}
|
web服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @RestController public class TestController {
@Autowired Environment env; @Autowired RemoteService remoteService;
@GetMapping("/provider1/{id}") public Object getTest(@PathVariable(required = false) Integer id, @RequestParam(required = false) String username) { return env.getProperty("local.server.port"); }
@PostMapping("/provider1") public Object postTest(@RequestBody Map<String, String> params) { System.out.println(params); return env.getProperty("local.server.port"); }
@GetMapping("/remote/get") public Object remoteGetTest() { Object provider2 = remoteService.getProvider2(); System.out.println("remote load data from provider2:" + provider2); return provider2; } }
|
服务提供者2(服务提供)
provider2和provider1类似,这里只改写下web实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @RestController public class TestController {
@Autowired Environment env;
@GetMapping("/provider2") public Object getTest() {
String port = env.getProperty("local.server.port"); JSONObject jsonObject = new JSONObject(); jsonObject.put("port", port);
System.out.println("idc-provider2:" + port);
return jsonObject; } }
|
Feign中集成了Ribbon负载均衡。这里可以用不同端口启动2个provider2实例, 然后通过provider1调用provider2,可以看到轮询输出不同的端口。
网关服务
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| <?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>spring-cloud-learning</artifactId> <groupId>cn.idea360</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion>
<artifactId>idc-gateway</artifactId>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <scope>compile</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.51</version> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
</dependencies>
<build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> </plugin> </plugins> </build>
</project>
|
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| server: port: 2000 spring: application: name: idc-gateway redis: host: localhost port: 6379 timeout: 6000ms jedis: pool: max-active: 1000 max-wait: -1ms max-idle: 10 min-idle: 5 cloud: consul: host: localhost port: 8500 gateway: discovery: locator: enabled: true routes: - id: provider1 uri: lb://idc-provider1 predicates: - Path=/p/** filters: - StripPrefix=1
logging: level: org.springframework.cloud.gateway: DEBUG reactor.netty.http.client: DEBUG
|
启动类
1 2 3 4 5 6 7 8
| @SpringBootApplication @EnableDiscoveryClient public class GatewayApp { public static void main(String[] args) { SpringApplication.run(GatewayApp.class, args); }
}
|
结语
到这里基本环境就搭建完毕了,下节做过滤器的相关实现。感谢大家阅读,欢迎关注公众号【当我遇上你】学习交流。