SpringCloud Alibaba微服务--Gateway使用
一、API 网关 (Gateway) 简介
1、什么是gateway
spring cloud gateway是一个服务器端的入口点,作为客户端和后端服务之间的中间层,负责请求路由、组合和协议转换。它充当系统的"前门",所有客户端请求都首先通过API网关,然后被路由到适当的后端服务。它是建立在 Spring Boot 2.x、 Spring WebFlux 和 Project Reactor之上,旨在为微服务架构提供简单、有效和统一的API路由管理方式
2、核心功能
- 请求路由:将客户端请求转发到相应的后端服务
- 协议转换:处理不同协议之间的转换(如HTTP到gRPC)
- 负载均衡:在多个服务实例之间分配请求
- 认证授权:验证请求的合法性,检查访问权限
- 限流熔断:控制请求速率,防止系统过载
- 缓存:缓存常用响应,减少后端压力
- 监控日志:记录请求指标和日志用于分析和监控
- 请求/响应转换:修改请求或响应内容
3、gateway在微服务中的优势
- 解耦客户端与服务端:客户端只需知道网关地址,无需了解内部服务结构
- 简化客户端:网关可以聚合多个服务请求,减少客户端调用次数
- 统一安全控制:集中处理认证、授权和安全策略
- 提高性能:通过缓存、压缩和协议优化提升响应速度
- 弹性设计:通过熔断、降级等机制提高系统可靠性
- 易于监控:集中收集所有API调用的指标和日志
4、gateway工作流程
客户端向 Spring Cloud Gateway 发出请求。如果Gateway处理程序映射确定一个请求与路由相匹配,它将被发送到Gateway Web处理程序。这个处理程序通过一个特定于该请求的过滤器链来运行该请求。过滤器被虚线分割的原因是,过滤器可以在代理请求发送之前和之后运行逻辑。所有的
"pre"
(前)过滤器逻辑都被执行。然后发出代理请求。在代理请求发出后,"post"
(后)过滤器逻辑被运行。
5、Zuul实现SpringCloud网关及不足之处
在SpringCloud中网关的实现包括两种gateway、zuul。Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。二者主要区别:
对比项 | Spring Cloud Gateway | Netflix Zuul |
---|---|---|
架构模型 | 基于 Reactor 和 Netty 的 异步非阻塞 模型 | 基于 Servlet 的 同步阻塞 模型 |
性能 | 更高吞吐量,支持 长连接(WebFlux) | 性能较低,受限于 Servlet 阻塞 IO |
依赖 | 需要 Spring WebFlux 和 Project Reactor | 依赖 Spring MVC 和 Servlet API |
功能扩展 | 支持 自定义过滤器、断言 和 全局过滤器 | 主要依赖 Groovy 脚本扩展过滤器 |
协议支持 | 支持 HTTP/2、WebSocket、gRPC | 仅支持 HTTP/1.x |
服务发现 | 原生支持 Eureka、Consul、Nacos | 需配合 Netflix Eureka 使用 |
负载均衡 | 集成 Ribbon 或 Spring Cloud LoadBalancer | 依赖 Ribbon |
熔断限流 | 支持 Hystrix、Resilience4j、Sentinel | 主要依赖 Hystrix |
配置方式 | YAML/Properties + Java DSL(更灵活) | 主要依赖 Groovy 脚本或 Java 配置 |
社区支持 | Spring 官方维护,持续更新 | Netflix 已停止维护(Zuul 1.x) |
适用场景 | 高并发、低延迟 的微服务网关 | 传统 同步阻塞 架构的简单网关 |
二、构建SpringCloud Gateway服务
1、新建子模块jd-shop-gateway服务
微服务搭建以及nacos注册和配置方式可以参考之前的文章:
SpringCloud Alibaba微服务框架搭建-CSDN博客
SpringCloud Alibaba微服务--Nacos注册中心和配置中心应用-CSDN博客
2、引入依赖
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId><version>3.0.7</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>${spring-cloud-alibaba.version}</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>${spring-cloud-alibaba.version}</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId><version>3.0.2</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><dependency><groupId>com.mdx</groupId><artifactId>mdx-shop-common</artifactId><version>1.0.0</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-loadbalancer</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-commons</artifactId></dependency></dependencies>
3、编写基础配置和路由规则
(1)路由基础配置
路由id:路由的唯一标示
路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
路由断言(predicates):判断路由的规则,
路由过滤器(filters):对请求或响应做处理
(2)设置bootstrap.yml配置文件
server:port: 8091spring:application:name: jd-shop-gatewaycloud:nacos:discovery:server-addr: 127.0.0.1:8848namespace: jdconfig:server-addr: 127.0.0.1:8848extension-configs:- data-id: ${spring.application.name}.yamlgroup: DEFAULT_GROUPrefresh: truefile-extension: ymlnamespace: jdgroup: DEFAULT_GROUPgateway:discovery:locator:enabled: truelower-case-service-id: trueroutes:- id: mdx-shop-user #路由的ID,没有固定规则但要求唯一,建议配合服务名uri: http://127.0.0.1:8021 #匹配后提供服务的路由地址predicates:- Path=/user/** #断言,路径相匹配的进行路由- id: mdx-shop-orderuri: http://127.0.0.1:8081predicates:- Path=/order/**main:web-application-type: reactive
4、重启服务,测试访问接口
通过gateway访问user服务,访问地址http://localhost:8091/user/getOrderNo?userId=T1234
8091为网关端口号
通过gateway访问order服务,访问地址http://127.0.0.1:8091/order/getOrderId?userId=T1234
8091为网关端口号
5、通过微服务名称的形式进行路由
将uri: http://127.0.0.1:8021 换成 uri: lb://jd-shop-user形式
重启服务,在测试一下user服务接口,访问
http://localhost:8091/user/getOrderNo?userId=T1234
8091为网关端口号
6、测试负载均衡
采用这种路由方式 uri: lb://jd-shop-user
在gateway添加配置:
开启通过服务中心的自动根据 serviceId 创建路由的功能
discovery:
locator:
enabled: true
lower-case-service-id: true
启动两个order服务,为另一个order服务新开一个端口号
启动两个order服务
nacos中可以看到有两个order服务实例
通过gateway访问order服务,访问地址http://127.0.0.1:8091/order/getOrderId?userId=T1234
可以发现访问的是OrderApplication服务接口
再次访问接口,发现访问的是OrderApplication2服务接口,实现了简单的负载均衡
三、通过nacos实现动态路由
对于微服务来说,如果使用配置文件来管理路由规则的话,每增加一个服务或者修改一个服务,都需要重启gateway服务,这样很麻烦,因此我们通过nacos来动态配置路由,就不需要进行服务重启了,编写对应的配置类,实现自动监听nacos配置文件的更新,实现动态刷新。
1、创建路由配置类
新建路由发布接口
public interface RouteService {/*** 添加路由信息* @return*/void addRoute(RouteDefinition routeDefinition);/*** 修改路由信息* @return*/void updateRoute(RouteDefinition routeDefinition);/*** 删除路由信息* @return*/void deleteRoute(String routeId);
}
新建RouteServiceImpl类实现接口
@Service
@Slf4j
public class RouteServiceImpl implements RouteService, ApplicationEventPublisherAware {@Autowiredprivate RouteDefinitionWriter routeDefinitionWriter;/*** 事件发布者*/private ApplicationEventPublisher publisher;@Overridepublic void addRoute(RouteDefinition routeDefinition) {log.info("添加路由:{}", routeDefinition);routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();this.publisher.publishEvent(new RefreshRoutesEvent(this));}@Overridepublic void updateRoute(RouteDefinition routeDefinition) {log.info("更新路由:{}", routeDefinition);routeDefinitionWriter.delete(Mono.just(routeDefinition.getId())).subscribe();routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();this.publisher.publishEvent(new RefreshRoutesEvent(this));}@Overridepublic void deleteRoute(String routeId) {log.info("删除路由:{}", routeId);routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();this.publisher.publishEvent(new RefreshRoutesEvent(this));}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.publisher = applicationEventPublisher;}
}
2、在nacos创建gateway-routes配置文件
(1)新建配置
json内容对应着RouteDefinition 类,Json内容:
[{"predicates":[{"args":{"pattern":"/order/**"},"name":"Path"}],"id":"jd-shop-order","uri":"lb://jd-shop-order","order":1},{"predicates":[{"args":{"pattern":"/user/**"},"name":"Path"}],"id":"jd-shop-user","uri":"lb://jd-shop-user","order":2}
]
这里面博主没有加过滤链:
"filters":[
{
"args":{
"parts":1
},
"name":"StripPrefix"
}
],作用:当请求路径为
/api/user
时,网关会截掉第一个路径部分(/api
),将请求转发到后端服务时路径变为/user
。
(2)修改bootstrap.yml配置文件,添加路由配置文件dataId、group等,之前路由规则删除或注释掉。
server:port: 8091spring:application:name: jd-shop-gatewaycloud:nacos:discovery:server-addr: 127.0.0.1:8848namespace: jdconfig:server-addr: 127.0.0.1:8848extension-configs:- data-id: ${spring.application.name}.yamlgroup: DEFAULT_GROUPrefresh: truefile-extension: ymlnamespace: jdgroup: DEFAULT_GROUPgateway:discovery:locator:enabled: truelower-case-service-id: true # routes: # - id: jd-shop-user #路由的ID,没有固定规则但要求唯一,建议配合服务名 # uri: lb://jd-shop-user #匹配后提供服务的路由地址 ## uri: http://127.0.0.1:8021 # predicates: # - Path=/user/** #断言,路径相匹配的进行路由 # # - id: jd-shop-order # uri: lb://jd-shop-order ## uri: http://127.0.0.1:8081 # predicates: # - Path=/order/**main:web-application-type: reactivegateway:routes:config:data-id: gateway-routes #动态路由group: DEFAULT_GROUPnamespace: jd
3、创建路由相关配置类
(1)创建配置类引入配置
@ConfigurationProperties(prefix = "gateway.routes.config")
@Component
@Data
public class GatewayRoutesConfigProperties {private String dataId;private String group;private String namespace;
}
(2)实例化nacos的ConfigService,交由springbean管理
@Configuration
public class GatewayServiceConfig {@Autowiredprivate GatewayRoutesConfigProperties configProperties;@Autowiredprivate NacosConfigProperties nacosConfigProperties;@Beanpublic ConfigService configService() throws NacosException {Properties properties = new Properties();properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosConfigProperties.getServerAddr());properties.setProperty(PropertyKeyConst.NAMESPACE, configProperties.getNamespace());return NacosFactory.createConfigService(properties);}}
(3)路由监听类实现,项目启动时会加载这个类
@PostConstruc 注解的作用,在spring bean的生命周期依赖注入完成后被调用的方法
@Component
@Slf4j
@RefreshScope
public class GatewayRouteInitConfig {@Autowiredprivate GatewayRoutesConfigProperties configProperties;@Autowiredprivate NacosConfigProperties nacosConfigProperties;@Autowiredprivate RouteService routeService;/*** nacos 配置服务*/@Autowiredprivate ConfigService configService;/*** JSON 转换对象*/private final ObjectMapper objectMapper = new ObjectMapper();@PostConstructpublic void init() {log.info("开始网关动态路由初始化...");try {// getConfigAndSignListener()方法 发起长轮询和对dataId数据变更注册监听的操作// getConfig 只是发送普通的HTTP请求String initConfigInfo = configService.getConfigAndSignListener(configProperties.getDataId(), configProperties.getGroup(), nacosConfigProperties.getTimeout(), new Listener() {@Overridepublic Executor getExecutor() {return null;}@Overridepublic void receiveConfigInfo(String configInfo) {if (StringUtils.isNotEmpty(configInfo)) {log.info("接收到网关路由更新配置:\r\n{}", configInfo);List<RouteDefinition> routeDefinitions = null;try {routeDefinitions = objectMapper.readValue(configInfo, new TypeReference<List<RouteDefinition>>() {});} catch (JsonProcessingException e) {log.error("解析路由配置出错," + e.getMessage(), e);}for (RouteDefinition definition : Objects.requireNonNull(routeDefinitions)) {routeService.updateRoute(definition);}} else {log.warn("当前网关无动态路由相关配置");}}});log.info("获取网关当前动态路由配置:\r\n{}", initConfigInfo);if (StringUtils.isNotEmpty(initConfigInfo)) {List<RouteDefinition> routeDefinitions = objectMapper.readValue(initConfigInfo, new TypeReference<List<RouteDefinition>>() {});for (RouteDefinition definition : routeDefinitions) {routeService.addRoute(definition);}} else {log.warn("当前网关无动态路由相关配置");}log.info("结束网关动态路由初始化...");} catch (Exception e) {log.error("初始化网关路由时发生错误", e);}}
}
4、测试动态路由
(1)重启服务,通过gateway访问order服务,访问地址http://127.0.0.1:8091/order/getOrderId?userId=T1234
8091为网关端口号
(2)在nacos配置中添加user服务路由规则,点击发布后,gateway的监听器已经监听到配置的改动
在不重启gateway服务前提下,通过gateway访问user服务,访问地址http://localhost:8091/user/getOrderNo?userId=T12345
可以看到成功路由到对应服务
SpringCloud Gateway网关服务就介绍到这里,创作不易,记得点赞收藏哟
上一篇文章:
SpringCloud Alibaba微服务--Sentinel的使用-CSDN博客