第十一章 GetAway服务网关详解
所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等。:此时,如果有新的客户端分别调用 订单服务 和 商品服务,那么这个客户端需要维护新的ip和port。而新客户端调用这些微服务时候,微服务又需要解决进行认证鉴权、安全校验、跨域问题。如果微服务 订单服务 和 商品服务 可能有成百
目录
一、服务网关使用背景
如下图所示,订单服务 调用 商品服务 的 流程的变化从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,缺乏更易用的管控、配置方式。 |
zuul | Netflix开源的网关,功能丰富,使用JANA开发,易于二次开发 | zuul1.0 缺乏管控,无法动态配置;依颡组件较多;处理Http请求依赖的是Web容器,性能不如Nginx |
Spring cloud Gateway | Spring cloud Gateway Spring公司为了替换Zuul而开发的网关服务 | 无 |
- zuul 和 Spring cloud Gateway 区别
zuul | Spring 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 |
order | order: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时候, |
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
更多推荐
所有评论(0)